add .formatter.exs + format
This commit is contained in:
3
.formatter.exs
Normal file
3
.formatter.exs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[
|
||||||
|
inputs: ["{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
@@ -1,6 +1,7 @@
|
|||||||
defmodule Blanks do
|
defmodule Blanks do
|
||||||
def replace(ast, replacements) do
|
def replace(ast, replacements) do
|
||||||
replacements = List.wrap(replacements)
|
replacements = List.wrap(replacements)
|
||||||
|
|
||||||
ast
|
ast
|
||||||
|> Macro.prewalk(replacements, &pre/2)
|
|> Macro.prewalk(replacements, &pre/2)
|
||||||
|> elem(0)
|
|> elem(0)
|
||||||
@@ -10,6 +11,7 @@ defmodule Blanks do
|
|||||||
{args, replacements} = Macro.prewalk(args, replacements, &pre_pin/2)
|
{args, replacements} = Macro.prewalk(args, replacements, &pre_pin/2)
|
||||||
{put_elem(node, 2, args), replacements}
|
{put_elem(node, 2, args), replacements}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp pre({:___, _, _}, [first | remainder]), do: {first, remainder}
|
defp pre({:___, _, _}, [first | remainder]), do: {first, remainder}
|
||||||
defp pre(node, acc), do: {node, acc}
|
defp pre(node, acc), do: {node, acc}
|
||||||
|
|
||||||
@@ -21,6 +23,7 @@ defmodule Blanks do
|
|||||||
^unquote(var)
|
^unquote(var)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp pin(var), do: var
|
defp pin(var), do: var
|
||||||
|
|
||||||
def count(ast) do
|
def count(ast) do
|
||||||
@@ -29,16 +32,18 @@ defmodule Blanks do
|
|||||||
|> elem(1)
|
|> elem(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp count({:___, _, _} = node, acc), do: {node, acc+1}
|
defp count({:___, _, _} = node, acc), do: {node, acc + 1}
|
||||||
defp count(node, acc), do: {node, acc}
|
defp count(node, acc), do: {node, acc}
|
||||||
|
|
||||||
def replace_line({:__block__, meta, lines}, replacement_fn) do
|
def replace_line({:__block__, meta, lines}, replacement_fn) do
|
||||||
replaced_lines = Enum.map(lines, fn(line) ->
|
replaced_lines =
|
||||||
replace_line(line, replacement_fn)
|
Enum.map(lines, fn line ->
|
||||||
end)
|
replace_line(line, replacement_fn)
|
||||||
|
end)
|
||||||
|
|
||||||
{:__block__, meta, replaced_lines}
|
{:__block__, meta, replaced_lines}
|
||||||
end
|
end
|
||||||
|
|
||||||
def replace_line(line, replacement_fn) do
|
def replace_line(line, replacement_fn) do
|
||||||
if count(line) > 0 do
|
if count(line) > 0 do
|
||||||
replacement_fn.(line)
|
replacement_fn.(line)
|
||||||
|
@@ -17,33 +17,34 @@ defmodule Display do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast(:clear_screen, state = %{clear_screen: true}) do
|
def handle_cast(:clear_screen, state = %{clear_screen: true}) do
|
||||||
IO.puts(ANSI.clear)
|
IO.puts(ANSI.clear())
|
||||||
IO.puts(ANSI.home)
|
IO.puts(ANSI.home())
|
||||||
|
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast(:clear_screen, state) do
|
def handle_cast(:clear_screen, state) do
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
def invalid_koan(koan, modules) do
|
def invalid_koan(koan, modules) do
|
||||||
Notifications.invalid_koan(koan, modules)
|
Notifications.invalid_koan(koan, modules)
|
||||||
|> IO.puts
|
|> IO.puts()
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_failure(failure, module, name) do
|
def show_failure(failure, module, name) do
|
||||||
format(failure, module, name)
|
format(failure, module, name)
|
||||||
|> IO.puts
|
|> IO.puts()
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_compile_error(error) do
|
def show_compile_error(error) do
|
||||||
Failure.show_compile_error(error)
|
Failure.show_compile_error(error)
|
||||||
|> IO.puts
|
|> IO.puts()
|
||||||
end
|
end
|
||||||
|
|
||||||
def congratulate do
|
def congratulate do
|
||||||
Notifications.congratulate
|
Notifications.congratulate()
|
||||||
|> IO.puts
|
|> IO.puts()
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_screen do
|
def clear_screen do
|
||||||
@@ -52,9 +53,9 @@ defmodule Display do
|
|||||||
|
|
||||||
defp format(failure, module, name) do
|
defp format(failure, module, name) do
|
||||||
"""
|
"""
|
||||||
#{Intro.intro(module, Tracker.visited)}
|
#{Intro.intro(module, Tracker.visited())}
|
||||||
Now meditate upon #{format_module(module)}
|
Now meditate upon #{format_module(module)}
|
||||||
#{ProgressBar.progress_bar(Tracker.summarize)}
|
#{ProgressBar.progress_bar(Tracker.summarize())}
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
#{name}
|
#{name}
|
||||||
#{Failure.format_failure(failure)}
|
#{Failure.format_failure(failure)}
|
||||||
@@ -62,6 +63,6 @@ defmodule Display do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp format_module(module) do
|
defp format_module(module) do
|
||||||
Module.split(module) |> List.last
|
Module.split(module) |> List.last()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -5,7 +5,7 @@ defmodule Display.Paint do
|
|||||||
def yellow(str), do: painter().yellow(str)
|
def yellow(str), do: painter().yellow(str)
|
||||||
|
|
||||||
defp painter do
|
defp painter do
|
||||||
case Mix.env do
|
case Mix.env() do
|
||||||
:test -> Display.Uncoloured
|
:test -> Display.Uncoloured
|
||||||
_ -> Display.Colours
|
_ -> Display.Colours
|
||||||
end
|
end
|
||||||
@@ -15,13 +15,13 @@ end
|
|||||||
defmodule Display.Colours do
|
defmodule Display.Colours do
|
||||||
alias IO.ANSI
|
alias IO.ANSI
|
||||||
|
|
||||||
def red(str), do: colourize(ANSI.red, str)
|
def red(str), do: colourize(ANSI.red(), str)
|
||||||
def cyan(str), do: colourize(ANSI.cyan, str)
|
def cyan(str), do: colourize(ANSI.cyan(), str)
|
||||||
def green(str), do: colourize(ANSI.green, str)
|
def green(str), do: colourize(ANSI.green(), str)
|
||||||
def yellow(str), do: colourize(ANSI.yellow, str)
|
def yellow(str), do: colourize(ANSI.yellow(), str)
|
||||||
|
|
||||||
defp colourize(color, message) do
|
defp colourize(color, message) do
|
||||||
Enum.join([color, message, ANSI.reset], "")
|
Enum.join([color, message, ANSI.reset()], "")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -3,12 +3,17 @@ defmodule Display.Failure do
|
|||||||
|
|
||||||
@no_value :ex_unit_no_meaningful_value
|
@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.cyan("Assertion failed in #{file}:#{line}")}
|
||||||
#{Paint.red(message)}
|
#{Paint.red(message)}
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_failure(%{error: %ExUnit.AssertionError{expr: expr} = error, file: file, line: line}) do
|
def format_failure(%{error: %ExUnit.AssertionError{expr: expr} = error, file: file, line: line}) do
|
||||||
"""
|
"""
|
||||||
#{Paint.cyan("Assertion failed in #{file}:#{line}")}
|
#{Paint.cyan("Assertion failed in #{file}:#{line}")}
|
||||||
@@ -16,6 +21,7 @@ defmodule Display.Failure do
|
|||||||
"""
|
"""
|
||||||
|> format_inequality(error)
|
|> format_inequality(error)
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_failure(%{error: error, file: file, line: line}) do
|
def format_failure(%{error: error, file: file, line: line}) do
|
||||||
"""
|
"""
|
||||||
#{Paint.cyan("Error in #{file}:#{line}")}
|
#{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
|
defp format_inequality(message, %{left: @no_value, right: @no_value}) do
|
||||||
message
|
message
|
||||||
end
|
end
|
||||||
|
|
||||||
defp format_inequality(message, %{left: @no_value, right: match_value}) do
|
defp format_inequality(message, %{left: @no_value, right: match_value}) do
|
||||||
"""
|
"""
|
||||||
#{message}
|
#{message}
|
||||||
value does not match: #{match_value |> inspect |> Paint.yellow}
|
value does not match: #{match_value |> inspect |> Paint.yellow()}
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
defp format_inequality(message, %{left: left, right: right}) do
|
defp format_inequality(message, %{left: left, right: right}) do
|
||||||
"""
|
"""
|
||||||
#{message}
|
#{message}
|
||||||
left: #{left |> inspect |> Paint.yellow}
|
left: #{left |> inspect |> Paint.yellow()}
|
||||||
right: #{right |> inspect |> Paint.yellow}
|
right: #{right |> inspect |> Paint.yellow()}
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
defp format_error(error) do
|
defp format_error(error) do
|
||||||
trace = System.stacktrace |> Enum.take(2)
|
trace = System.stacktrace() |> Enum.take(2)
|
||||||
Paint.red(Exception.format(:error, error, trace))
|
Paint.red(Exception.format(:error, error, trace))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ defmodule Display.Intro do
|
|||||||
alias Display.Paint
|
alias Display.Paint
|
||||||
|
|
||||||
def intro(module, modules) do
|
def intro(module, modules) do
|
||||||
if not module in modules do
|
if not (module in modules) do
|
||||||
show_intro(module.intro)
|
show_intro(module.intro)
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
@@ -10,7 +10,7 @@ defmodule Display.Intro do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def show_intro(message) do
|
def show_intro(message) do
|
||||||
message <> "\n"
|
(message <> "\n")
|
||||||
|> Paint.green
|
|> Paint.green()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -15,7 +15,7 @@ defmodule Display.Notifications do
|
|||||||
|> Enum.map(&Atom.to_string/1)
|
|> Enum.map(&Atom.to_string/1)
|
||||||
|> Enum.map(&name/1)
|
|> Enum.map(&name/1)
|
||||||
|> Enum.join(", ")
|
|> Enum.join(", ")
|
||||||
|> Paint.red
|
|> Paint.red()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp name("Elixir." <> module), do: module
|
defp name("Elixir." <> module), do: module
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
defmodule Display.ProgressBar do
|
defmodule Display.ProgressBar do
|
||||||
|
|
||||||
@progress_bar_length 30
|
@progress_bar_length 30
|
||||||
|
|
||||||
def progress_bar(%{current: current, total: total}) do
|
def progress_bar(%{current: current, total: total}) do
|
||||||
@@ -9,11 +8,12 @@ defmodule Display.ProgressBar do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp calculate_progress(current, total) do
|
defp calculate_progress(current, total) do
|
||||||
round( (current/total) * @progress_bar_length)
|
round(current / total * @progress_bar_length)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_arrow(0), do: ""
|
defp build_arrow(0), do: ""
|
||||||
|
|
||||||
defp build_arrow(length) do
|
defp build_arrow(length) do
|
||||||
String.duplicate("=", length-1) <> ">"
|
String.duplicate("=", length - 1) <> ">"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
defmodule Execute do
|
defmodule Execute do
|
||||||
def run_module(module, callback \\ fn(_result, _module, _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(module, koan, callback)
|
|> hook(module, koan, callback)
|
||||||
@@ -24,7 +24,7 @@ defmodule Execute do
|
|||||||
|
|
||||||
def listen_for_result(module, name) do
|
def listen_for_result(module, name) do
|
||||||
receive do
|
receive do
|
||||||
:ok -> :passed
|
:ok -> :passed
|
||||||
%{error: _} = failure -> {:failed, failure, module, name}
|
%{error: _} = failure -> {:failed, failure, module, name}
|
||||||
_ -> listen_for_result(module, name)
|
_ -> listen_for_result(module, name)
|
||||||
end
|
end
|
||||||
@@ -32,23 +32,25 @@ defmodule Execute do
|
|||||||
|
|
||||||
defp exec(module, name, args, parent) do
|
defp exec(module, name, args, parent) do
|
||||||
result = apply(module, name, args)
|
result = apply(module, name, args)
|
||||||
send parent, expand(result, module)
|
send(parent, expand(result, module))
|
||||||
Process.exit(self(), :kill)
|
Process.exit(self(), :kill)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp expand(:ok, _), do: :ok
|
defp expand(:ok, _), do: :ok
|
||||||
|
|
||||||
defp expand(error, module) do
|
defp expand(error, module) do
|
||||||
{file, line} = System.stacktrace
|
{file, line} =
|
||||||
|> Enum.drop_while(&!in_koan?(&1, module))
|
System.stacktrace()
|
||||||
|> List.first
|
|> Enum.drop_while(&(!in_koan?(&1, module)))
|
||||||
|
|> List.first()
|
||||||
|> extract_file_and_line
|
|> extract_file_and_line
|
||||||
|
|
||||||
%{error: error, file: file, line: line}
|
%{error: error, file: file, line: line}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp in_koan?({module, _, _, _}, koan), do: module == koan
|
defp in_koan?({module, _, _, _}, koan), do: module == koan
|
||||||
|
|
||||||
defp extract_file_and_line({_, _, _, [file: file, line: line]}) do
|
defp extract_file_and_line({_, _, _, [file: file, line: line]}) do
|
||||||
{file, line}
|
{file, line}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
14
lib/koans.ex
14
lib/koans.ex
@@ -29,8 +29,10 @@ defmodule Koans do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defmacro generate_test_method(_name, 0, _body), do: false
|
defmacro generate_test_method(_name, 0, _body), do: false
|
||||||
|
|
||||||
defmacro generate_test_method(name, 1, body) do
|
defmacro generate_test_method(name, 1, body) do
|
||||||
single_var = Blanks.replace(body, Macro.var(:answer, __MODULE__))
|
single_var = Blanks.replace(body, Macro.var(:answer, __MODULE__))
|
||||||
|
|
||||||
quote do
|
quote do
|
||||||
def unquote(name)(answer) do
|
def unquote(name)(answer) do
|
||||||
try do
|
try do
|
||||||
@@ -42,8 +44,11 @@ defmodule Koans do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defmacro generate_test_method(name, number_of_args, body) do
|
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)
|
multi_var = Blanks.replace(body, answer_vars)
|
||||||
|
|
||||||
quote do
|
quote do
|
||||||
@@ -60,12 +65,12 @@ defmodule Koans do
|
|||||||
|
|
||||||
defp blank_line_replacement({:assert, _meta, [expr]}) do
|
defp blank_line_replacement({:assert, _meta, [expr]}) do
|
||||||
code = Macro.escape(expr)
|
code = Macro.escape(expr)
|
||||||
quote do: raise ExUnit.AssertionError, expr: unquote(code)
|
quote do: raise(ExUnit.AssertionError, expr: unquote(code))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp blank_line_replacement(line) do
|
defp blank_line_replacement(line) do
|
||||||
code = Macro.escape(line)
|
code = Macro.escape(line)
|
||||||
quote do: raise ExUnit.AssertionError, expr: unquote(code)
|
quote do: raise(ExUnit.AssertionError, expr: unquote(code))
|
||||||
end
|
end
|
||||||
|
|
||||||
defmacro __using__(_opts) do
|
defmacro __using__(_opts) do
|
||||||
@@ -83,6 +88,7 @@ defmodule Koans do
|
|||||||
|
|
||||||
defmacro __before_compile__(env) do
|
defmacro __before_compile__(env) do
|
||||||
koans = koans(env)
|
koans = koans(env)
|
||||||
|
|
||||||
quote do
|
quote do
|
||||||
def all_koans do
|
def all_koans do
|
||||||
unquote(koans)
|
unquote(koans)
|
||||||
@@ -95,6 +101,6 @@ defmodule Koans do
|
|||||||
defp koans(env) do
|
defp koans(env) do
|
||||||
env.module
|
env.module
|
||||||
|> Module.get_attribute(:koans)
|
|> Module.get_attribute(:koans)
|
||||||
|> Enum.reverse
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
defmodule Equalities do
|
defmodule Equalities do
|
||||||
use Koans
|
use Koans
|
||||||
|
|
||||||
@intro """
|
@intro """
|
||||||
Welcome to the Elixir koans.
|
Welcome to the Elixir koans.
|
||||||
Let these be your first humble steps towards learning a new language.
|
Let these be your first humble steps towards learning a new language.
|
||||||
|
|
||||||
|
@@ -11,12 +11,12 @@ defmodule Atoms do
|
|||||||
koan "It is surprising to find out that booleans are atoms" do
|
koan "It is surprising to find out that booleans are atoms" do
|
||||||
assert is_atom(true) == ___
|
assert is_atom(true) == ___
|
||||||
assert is_boolean(false) == ___
|
assert is_boolean(false) == ___
|
||||||
assert :true == ___
|
assert true == ___
|
||||||
assert :false == ___
|
assert false == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Like booleans, the nil value is also an atom" do
|
koan "Like booleans, the nil value is also an atom" do
|
||||||
assert is_atom(nil) == ___
|
assert is_atom(nil) == ___
|
||||||
assert :nil == ___
|
assert nil == ___
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -6,13 +6,11 @@ defmodule Maps do
|
|||||||
@person %{
|
@person %{
|
||||||
first_name: "Jon",
|
first_name: "Jon",
|
||||||
last_name: "Snow",
|
last_name: "Snow",
|
||||||
age: 27,
|
age: 27
|
||||||
}
|
}
|
||||||
|
|
||||||
koan "Maps represent structured data, like a person" do
|
koan "Maps represent structured data, like a person" do
|
||||||
assert @person == %{first_name: ___,
|
assert @person == %{first_name: ___, last_name: "Snow", age: 27}
|
||||||
last_name: "Snow",
|
|
||||||
age: 27 }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Fetching a value returns a tuple with ok when it exists" do
|
koan "Fetching a value returns a tuple with ok when it exists" do
|
||||||
|
@@ -49,7 +49,7 @@ defmodule Structs do
|
|||||||
|
|
||||||
koan "Use the update_in macro to modify a nested value" do
|
koan "Use the update_in macro to modify a nested value" do
|
||||||
airline = %Airline{plane: %Plane{maker: :boeing, passengers: 200}}
|
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
|
end
|
||||||
|
|
||||||
koan "Use the put_in macro with atoms to replace a nested value in a non-struct" do
|
koan "Use the put_in macro with atoms to replace a nested value in a non-struct" do
|
||||||
|
@@ -17,7 +17,7 @@ defmodule Sigils do
|
|||||||
end
|
end
|
||||||
|
|
||||||
koan "The lowercase ~s sigil supports string interpolation" do
|
koan "The lowercase ~s sigil supports string interpolation" do
|
||||||
assert ~s[1 + 1 = #{1+1}] == ___
|
assert ~s[1 + 1 = #{1 + 1}] == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "The ~S sigil is similar to ~s but doesn't do interpolation" do
|
koan "The ~S sigil is similar to ~s but doesn't do interpolation" do
|
||||||
@@ -29,7 +29,7 @@ defmodule Sigils do
|
|||||||
end
|
end
|
||||||
|
|
||||||
koan "The ~w sigil also allows interpolation" do
|
koan "The ~w sigil also allows interpolation" do
|
||||||
assert ~w(Hello 1#{1+1}3) == ___
|
assert ~w(Hello 1#{1 + 1}3) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "The ~W sigil behaves to ~w as ~S behaves to ~s" do
|
koan "The ~W sigil behaves to ~w as ~S behaves to ~s" do
|
||||||
|
@@ -41,7 +41,7 @@ defmodule PatternMatching do
|
|||||||
|
|
||||||
koan "Lists must match exactly" do
|
koan "Lists must match exactly" do
|
||||||
assert_raise ___, fn ->
|
assert_raise ___, fn ->
|
||||||
[a, b] = [1,2,3]
|
[a, b] = [1, 2, 3]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -71,9 +71,9 @@ defmodule PatternMatching do
|
|||||||
|
|
||||||
koan "And they will only run the code that matches the argument" do
|
koan "And they will only run the code that matches the argument" do
|
||||||
name = fn
|
name = fn
|
||||||
("duck") -> "Donald"
|
"duck" -> "Donald"
|
||||||
("mouse") -> "Mickey"
|
"mouse" -> "Mickey"
|
||||||
(_other) -> "I need a name!"
|
_other -> "I need a name!"
|
||||||
end
|
end
|
||||||
|
|
||||||
assert name.("mouse") == ___
|
assert name.("mouse") == ___
|
||||||
@@ -84,10 +84,11 @@ defmodule PatternMatching do
|
|||||||
koan "Errors are shaped differently than successful results" do
|
koan "Errors are shaped differently than successful results" do
|
||||||
dog = %{type: "dog"}
|
dog = %{type: "dog"}
|
||||||
|
|
||||||
result = case Map.fetch(dog, :type) do
|
result =
|
||||||
{:ok, value} -> value
|
case Map.fetch(dog, :type) do
|
||||||
:error -> "not present"
|
{:ok, value} -> value
|
||||||
end
|
:error -> "not present"
|
||||||
|
end
|
||||||
|
|
||||||
assert result == ___
|
assert result == ___
|
||||||
end
|
end
|
||||||
@@ -133,10 +134,11 @@ defmodule PatternMatching do
|
|||||||
pinned_variable = 1
|
pinned_variable = 1
|
||||||
|
|
||||||
example = fn
|
example = fn
|
||||||
(^pinned_variable) -> "The number One"
|
^pinned_variable -> "The number One"
|
||||||
(2) -> "The number Two"
|
2 -> "The number Two"
|
||||||
(number) -> "The number #{number}"
|
number -> "The number #{number}"
|
||||||
end
|
end
|
||||||
|
|
||||||
assert example.(1) == ___
|
assert example.(1) == ___
|
||||||
assert example.(2) == ___
|
assert example.(2) == ___
|
||||||
assert example.(3) == ___
|
assert example.(3) == ___
|
||||||
@@ -144,17 +146,20 @@ defmodule PatternMatching do
|
|||||||
|
|
||||||
koan "Pinning works anywhere one would match, including 'case'" do
|
koan "Pinning works anywhere one would match, including 'case'" do
|
||||||
pinned_variable = 1
|
pinned_variable = 1
|
||||||
result = case 1 do
|
|
||||||
^pinned_variable -> "same"
|
result =
|
||||||
other -> "different #{other}"
|
case 1 do
|
||||||
end
|
^pinned_variable -> "same"
|
||||||
|
other -> "different #{other}"
|
||||||
|
end
|
||||||
|
|
||||||
assert result == ___
|
assert result == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Trying to rebind a pinned variable will result in an error" do
|
koan "Trying to rebind a pinned variable will result in an error" do
|
||||||
a = 1
|
a = 1
|
||||||
assert_raise MatchError, fn() ->
|
|
||||||
|
assert_raise MatchError, fn ->
|
||||||
^a = ___
|
^a = ___
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -12,6 +12,7 @@ defmodule Functions do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def multiply(a, b), do: a * b
|
def multiply(a, b), do: a * b
|
||||||
|
|
||||||
koan "Single line functions are cool, but mind the comma and the colon!" do
|
koan "Single line functions are cool, but mind the comma and the colon!" do
|
||||||
assert 6 == multiply(2, ___)
|
assert 6 == multiply(2, ___)
|
||||||
end
|
end
|
||||||
@@ -41,8 +42,8 @@ defmodule Functions do
|
|||||||
assert sum_up(1) == ___
|
assert sum_up(1) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
def bigger(a,b) when a > b, do: "#{a} is 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}"
|
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
|
koan "Intricate guards are possible, but be mindful of the reader" do
|
||||||
assert bigger(10, 5) == ___
|
assert bigger(10, 5) == ___
|
||||||
@@ -58,13 +59,13 @@ defmodule Functions do
|
|||||||
end
|
end
|
||||||
|
|
||||||
koan "Little anonymous functions are common, and called with a dot" do
|
koan "Little anonymous functions are common, and called with a dot" do
|
||||||
multiply = fn (a,b) -> a * b end
|
multiply = fn a, b -> a * b end
|
||||||
assert multiply.(2,3) == ___
|
assert multiply.(2, 3) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "You can even go shorter, by using capture syntax `&()` and positional arguments" do
|
koan "You can even go shorter, by using capture syntax `&()` and positional arguments" do
|
||||||
multiply = &(&1 * &2)
|
multiply = &(&1 * &2)
|
||||||
assert multiply.(2,3) == ___
|
assert multiply.(2, 3) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Prefix a string with & to build a simple anonymous greet function" do
|
koan "Prefix a string with & to build a simple anonymous greet function" do
|
||||||
@@ -77,7 +78,7 @@ defmodule Functions do
|
|||||||
assert three_times.("foo") == ___
|
assert three_times.("foo") == ___
|
||||||
end
|
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
|
def square(number), do: number * number
|
||||||
|
|
||||||
koan "You can pass functions around as arguments. Place an '&' before the name and state the arity" do
|
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
|
end
|
||||||
|
|
||||||
koan "The result of a function can be piped into another function as its first argument" do
|
koan "The result of a function can be piped into another function as its first argument" do
|
||||||
result = "full-name"
|
result =
|
||||||
|> String.split("-")
|
"full-name"
|
||||||
|> Enum.map(&String.capitalize/1)
|
|> String.split("-")
|
||||||
|> Enum.join(" ")
|
|> Enum.map(&String.capitalize/1)
|
||||||
|
|> Enum.join(" ")
|
||||||
|
|
||||||
assert result == ___
|
assert result == ___
|
||||||
end
|
end
|
||||||
|
@@ -12,12 +12,14 @@ defmodule Enums do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def less_than_five?(n), do: n < 5
|
def less_than_five?(n), do: n < 5
|
||||||
|
|
||||||
koan "Elements can have a lot in common" do
|
koan "Elements can have a lot in common" do
|
||||||
assert Enum.all?([1, 2, 3], &less_than_five?/1) == ___
|
assert Enum.all?([1, 2, 3], &less_than_five?/1) == ___
|
||||||
assert Enum.all?([4, 6, 8], &less_than_five?/1) == ___
|
assert Enum.all?([4, 6, 8], &less_than_five?/1) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
def even?(n), do: rem(n, 2) == 0
|
def even?(n), do: rem(n, 2) == 0
|
||||||
|
|
||||||
koan "Sometimes you just want to know if there are any elements fulfilling a condition" do
|
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, 2, 3], &even?/1) == ___
|
||||||
assert Enum.any?([1, 3, 5], &even?/1) == ___
|
assert Enum.any?([1, 3, 5], &even?/1) == ___
|
||||||
@@ -30,6 +32,7 @@ defmodule Enums do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def multiply_by_ten(n), do: 10 * n
|
def multiply_by_ten(n), do: 10 * n
|
||||||
|
|
||||||
koan "Map converts each element of a list by running some function with it" do
|
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) == ___
|
assert Enum.map([1, 2, 3], &multiply_by_ten/1) == ___
|
||||||
end
|
end
|
||||||
@@ -67,6 +70,7 @@ defmodule Enums do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def divisible_by_five?(n), do: rem(n, 5) == 0
|
def divisible_by_five?(n), do: rem(n, 5) == 0
|
||||||
|
|
||||||
koan "...but you don't quite find it..." do
|
koan "...but you don't quite find it..." do
|
||||||
assert Enum.find([1, 2, 3], &divisible_by_five?/1) == ___
|
assert Enum.find([1, 2, 3], &divisible_by_five?/1) == ___
|
||||||
end
|
end
|
||||||
@@ -76,6 +80,6 @@ defmodule Enums do
|
|||||||
end
|
end
|
||||||
|
|
||||||
koan "Collapse an entire list of elements down to a single one by repeating a function." do
|
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
|
||||||
end
|
end
|
||||||
|
@@ -18,9 +18,11 @@ defmodule Processes do
|
|||||||
end
|
end
|
||||||
|
|
||||||
koan "New processes are spawned functions" do
|
koan "New processes are spawned functions" do
|
||||||
value = spawn(fn -> receive do
|
value =
|
||||||
end
|
spawn(fn ->
|
||||||
end)
|
receive do
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
assert is_pid(value) == ___
|
assert is_pid(value) == ___
|
||||||
end
|
end
|
||||||
@@ -39,7 +41,7 @@ defmodule Processes do
|
|||||||
end
|
end
|
||||||
|
|
||||||
koan "Processes can send and receive messages" do
|
koan "Processes can send and receive messages" do
|
||||||
send self(), "hola!"
|
send(self(), "hola!")
|
||||||
|
|
||||||
receive do
|
receive do
|
||||||
msg -> assert msg == ___
|
msg -> assert msg == ___
|
||||||
@@ -58,8 +60,8 @@ defmodule Processes do
|
|||||||
end
|
end
|
||||||
|
|
||||||
koan "Received messages are queued, first in first out" do
|
koan "Received messages are queued, first in first out" do
|
||||||
send self(), "hola!"
|
send(self(), "hola!")
|
||||||
send self(), "como se llama?"
|
send(self(), "como se llama?")
|
||||||
|
|
||||||
assert_receive ___
|
assert_receive ___
|
||||||
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
|
koan "A common pattern is to include the sender in the message, so that it can reply" do
|
||||||
greeter = fn ->
|
greeter = fn ->
|
||||||
receive do
|
receive do
|
||||||
{:hello, sender} -> send sender, :how_are_you?
|
{:hello, sender} -> send(sender, :how_are_you?)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
pid = spawn(greeter)
|
pid = spawn(greeter)
|
||||||
|
|
||||||
send pid, {:hello, self()}
|
send(pid, {:hello, self()})
|
||||||
assert_receive ___
|
assert_receive ___
|
||||||
end
|
end
|
||||||
|
|
||||||
def yelling_echo_loop do
|
def yelling_echo_loop do
|
||||||
receive do
|
receive do
|
||||||
{caller, value} ->
|
{caller, value} ->
|
||||||
send caller, String.upcase(value)
|
send(caller, String.upcase(value))
|
||||||
yelling_echo_loop()
|
yelling_echo_loop()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -89,18 +91,19 @@ defmodule Processes do
|
|||||||
koan "Use tail recursion to receive multiple messages" do
|
koan "Use tail recursion to receive multiple messages" do
|
||||||
pid = spawn_link(&yelling_echo_loop/0)
|
pid = spawn_link(&yelling_echo_loop/0)
|
||||||
|
|
||||||
send pid, {self(), "o"}
|
send(pid, {self(), "o"})
|
||||||
assert_receive ___
|
assert_receive ___
|
||||||
|
|
||||||
send pid, {self(), "hai"}
|
send(pid, {self(), "hai"})
|
||||||
assert_receive ___
|
assert_receive ___
|
||||||
end
|
end
|
||||||
|
|
||||||
def state(value) do
|
def state(value) do
|
||||||
receive do
|
receive do
|
||||||
{caller, :get} ->
|
{caller, :get} ->
|
||||||
send caller, value
|
send(caller, value)
|
||||||
state(value)
|
state(value)
|
||||||
|
|
||||||
{caller, :set, new_value} ->
|
{caller, :set, new_value} ->
|
||||||
state(new_value)
|
state(new_value)
|
||||||
end
|
end
|
||||||
@@ -108,38 +111,45 @@ defmodule Processes do
|
|||||||
|
|
||||||
koan "Processes can be used to hold state" do
|
koan "Processes can be used to hold state" do
|
||||||
initial_state = "foo"
|
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 ___
|
assert_receive ___
|
||||||
|
|
||||||
send pid, {self(), :set, "bar"}
|
send(pid, {self(), :set, "bar"})
|
||||||
send pid, {self(), :get}
|
send(pid, {self(), :get})
|
||||||
assert_receive ___
|
assert_receive ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Waiting for a message can get boring" do
|
koan "Waiting for a message can get boring" do
|
||||||
parent = self()
|
parent = self()
|
||||||
spawn(fn -> receive do
|
|
||||||
after
|
spawn(fn ->
|
||||||
5 -> send parent, {:waited_too_long, "I am impatient"}
|
receive do
|
||||||
end
|
after
|
||||||
end)
|
5 -> send(parent, {:waited_too_long, "I am impatient"})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
assert_receive ___
|
assert_receive ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Trapping will allow you to react to someone terminating the process" do
|
koan "Trapping will allow you to react to someone terminating the process" do
|
||||||
parent = self()
|
parent = self()
|
||||||
pid = spawn(fn ->
|
|
||||||
Process.flag(:trap_exit, true)
|
pid =
|
||||||
send parent, :ready
|
spawn(fn ->
|
||||||
receive do
|
Process.flag(:trap_exit, true)
|
||||||
{:EXIT, _pid, reason} -> send parent, {:exited, reason}
|
send(parent, :ready)
|
||||||
end
|
|
||||||
end)
|
receive do
|
||||||
|
{:EXIT, _pid, reason} -> send(parent, {:exited, reason})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
receive do
|
receive do
|
||||||
:ready -> true
|
:ready -> true
|
||||||
|
@@ -10,23 +10,26 @@ defmodule Tasks do
|
|||||||
end
|
end
|
||||||
|
|
||||||
koan "If you don't need a result, use start_link/1" do
|
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 == ___
|
assert result == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Yield returns nil if the task isn't done yet" do
|
koan "Yield returns nil if the task isn't done yet" do
|
||||||
handle = Task.async(fn ->
|
handle =
|
||||||
:timer.sleep(100)
|
Task.async(fn ->
|
||||||
3 * 3
|
:timer.sleep(100)
|
||||||
end)
|
3 * 3
|
||||||
|
end)
|
||||||
|
|
||||||
assert Task.yield(handle, 10) == ___
|
assert Task.yield(handle, 10) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Tasks can be aborted with shutdown" do
|
koan "Tasks can be aborted with shutdown" do
|
||||||
handle = Task.async(fn ->
|
handle =
|
||||||
:timer.sleep(100)
|
Task.async(fn ->
|
||||||
3 * 3
|
:timer.sleep(100)
|
||||||
end)
|
3 * 3
|
||||||
|
end)
|
||||||
|
|
||||||
%Task{pid: pid} = handle
|
%Task{pid: pid} = handle
|
||||||
Task.shutdown(handle)
|
Task.shutdown(handle)
|
||||||
@@ -41,10 +44,11 @@ defmodule Tasks do
|
|||||||
end
|
end
|
||||||
|
|
||||||
koan "You can yield to multiple tasks at once and extract the results" do
|
koan "You can yield to multiple tasks at once and extract the results" do
|
||||||
squares = [1, 2, 3, 4]
|
squares =
|
||||||
|> Enum.map(fn(number) -> Task.async(fn -> number * number end) end)
|
[1, 2, 3, 4]
|
||||||
|> Task.yield_many(100)
|
|> Enum.map(fn number -> Task.async(fn -> number * number end) end)
|
||||||
|> Enum.map(fn({_task,{:ok, result}}) -> result end)
|
|> Task.yield_many(100)
|
||||||
|
|> Enum.map(fn {_task, {:ok, result}} -> result end)
|
||||||
|
|
||||||
assert squares == ___
|
assert squares == ___
|
||||||
end
|
end
|
||||||
|
@@ -5,36 +5,38 @@ defmodule Agents do
|
|||||||
|
|
||||||
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) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Agents may also be named so that you don't have to keep the pid around" do
|
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)
|
Agent.start_link(fn -> "Why hello" end, name: AgentSmith)
|
||||||
assert Agent.get(AgentSmith, &(&1)) == ___
|
assert Agent.get(AgentSmith, & &1) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Update to update the state" do
|
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)
|
String.upcase(old)
|
||||||
end)
|
end)
|
||||||
assert Agent.get(__MODULE__, &(&1)) == ___
|
|
||||||
|
assert Agent.get(__MODULE__, & &1) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Use get_and_update when you need to read and change a value in one go" do
|
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_list =
|
||||||
{old, ["Bread" | old]}
|
Agent.get_and_update(__MODULE__, fn old ->
|
||||||
end)
|
{old, ["Bread" | old]}
|
||||||
|
end)
|
||||||
|
|
||||||
assert old_list == ___
|
assert old_list == ___
|
||||||
assert Agent.get(__MODULE__, &(&1)) == ___
|
assert Agent.get(__MODULE__, & &1) == ___
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Somebody has to switch off the light at the end of the day" do
|
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__)
|
Agent.stop(__MODULE__)
|
||||||
|
|
||||||
|
@@ -46,7 +46,7 @@ defmodule GenServers do
|
|||||||
# GenServer implementation
|
# GenServer implementation
|
||||||
|
|
||||||
def handle_call(:get_password, _from, current_password) do
|
def handle_call(:get_password, _from, current_password) do
|
||||||
{:reply, current_password, current_password}
|
{:reply, current_password, current_password}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_call(:get_manufacturer, _from, current_state) do
|
def handle_call(:get_manufacturer, _from, current_state) do
|
||||||
@@ -73,6 +73,7 @@ defmodule GenServers do
|
|||||||
case password do
|
case password do
|
||||||
password when password === current_password ->
|
password when password === current_password ->
|
||||||
{:reply, {:ok, "Laptop unlocked!"}, current_password}
|
{:reply, {:ok, "Laptop unlocked!"}, current_password}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{:reply, {:error, "Incorrect password!"}, current_password}
|
{:reply, {:error, "Incorrect password!"}, current_password}
|
||||||
end
|
end
|
||||||
@@ -82,6 +83,7 @@ defmodule GenServers do
|
|||||||
case old_password do
|
case old_password do
|
||||||
old_password when old_password == current_password ->
|
old_password when old_password == current_password ->
|
||||||
{:noreply, new_password}
|
{:noreply, new_password}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{:noreply, current_password}
|
{:noreply, current_password}
|
||||||
end
|
end
|
||||||
@@ -147,7 +149,7 @@ defmodule GenServers do
|
|||||||
{_, response} = Laptop.unlock("EL!73")
|
{_, response} = Laptop.unlock("EL!73")
|
||||||
assert response == ___
|
assert response == ___
|
||||||
|
|
||||||
{_, response} = Laptop.owner_name
|
{_, response} = Laptop.owner_name()
|
||||||
assert response == ___
|
assert response == ___
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -3,7 +3,7 @@ defmodule Protocols do
|
|||||||
|
|
||||||
@intro "Want to follow the rules? Adhere to the protocol!"
|
@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
|
defimpl School, for: Any do
|
||||||
def enroll(_) do
|
def enroll(_) do
|
||||||
@@ -16,9 +16,9 @@ defmodule Protocols do
|
|||||||
defstruct name: ""
|
defstruct name: ""
|
||||||
end
|
end
|
||||||
|
|
||||||
defmodule Musician, do: defstruct name: "", instrument: ""
|
defmodule(Musician, do: defstruct(name: "", instrument: ""))
|
||||||
defmodule Dancer, do: defstruct name: "", dance_style: ""
|
defmodule(Dancer, do: defstruct(name: "", dance_style: ""))
|
||||||
defmodule Baker, do: defstruct name: ""
|
defmodule(Baker, do: defstruct(name: ""))
|
||||||
|
|
||||||
defimpl School, for: Musician do
|
defimpl School, for: Musician do
|
||||||
def enroll(musician) do
|
def enroll(musician) do
|
||||||
|
@@ -9,10 +9,11 @@ defmodule Mix.Tasks.Meditate do
|
|||||||
|
|
||||||
{parsed, _, _} = OptionParser.parse(args)
|
{parsed, _, _} = OptionParser.parse(args)
|
||||||
|
|
||||||
modules = parsed
|
modules =
|
||||||
|> initial_module
|
parsed
|
||||||
|> ok?
|
|> initial_module
|
||||||
|> Runner.modules_to_run
|
|> ok?
|
||||||
|
|> Runner.modules_to_run()
|
||||||
|
|
||||||
Tracker.set_total(modules)
|
Tracker.set_total(modules)
|
||||||
Tracker.notify_on_complete(self())
|
Tracker.notify_on_complete(self())
|
||||||
@@ -26,7 +27,7 @@ defmodule Mix.Tasks.Meditate do
|
|||||||
|
|
||||||
defp initial_module(parsed) do
|
defp initial_module(parsed) do
|
||||||
name = Keyword.get(parsed, :koan, "Equalities")
|
name = Keyword.get(parsed, :koan, "Equalities")
|
||||||
String.to_atom("Elixir."<> name)
|
String.to_atom("Elixir." <> name)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp set_clear_screen(parsed) do
|
defp set_clear_screen(parsed) do
|
||||||
@@ -39,7 +40,7 @@ defmodule Mix.Tasks.Meditate do
|
|||||||
if Runner.koan?(koan) do
|
if Runner.koan?(koan) do
|
||||||
koan
|
koan
|
||||||
else
|
else
|
||||||
Display.invalid_koan(koan, Runner.modules)
|
Display.invalid_koan(koan, Runner.modules())
|
||||||
exit(:normal)
|
exit(:normal)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -13,7 +13,8 @@ defmodule Runner do
|
|||||||
|
|
||||||
modules
|
modules
|
||||||
|> Stream.map(&(&1.module_info |> get_in([:compile, :source])))
|
|> 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.zip(modules)
|
||||||
|> Stream.filter(fn {_path, mod} -> koan?(mod) end)
|
|> Stream.filter(fn {_path, mod} -> koan?(mod) end)
|
||||||
|> Stream.map(fn {path, mod} -> {path_to_number(path), 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)
|
Display.show_failure(error, module, name)
|
||||||
:failed
|
:failed
|
||||||
end
|
end
|
||||||
|
|
||||||
defp display(_), do: :passed
|
defp display(_), do: :passed
|
||||||
|
|
||||||
defp flush do
|
defp flush do
|
||||||
|
@@ -2,9 +2,9 @@ defmodule Tracker do
|
|||||||
alias __MODULE__
|
alias __MODULE__
|
||||||
|
|
||||||
defstruct total: 0,
|
defstruct total: 0,
|
||||||
koans: MapSet.new(),
|
koans: MapSet.new(),
|
||||||
visited_modules: MapSet.new(),
|
visited_modules: MapSet.new(),
|
||||||
on_complete: :noop
|
on_complete: :noop
|
||||||
|
|
||||||
def start_link do
|
def start_link do
|
||||||
Agent.start_link(fn -> %Tracker{} end, name: __MODULE__)
|
Agent.start_link(fn -> %Tracker{} end, name: __MODULE__)
|
||||||
@@ -15,15 +15,17 @@ defmodule Tracker do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_total(modules) do
|
def set_total(modules) do
|
||||||
total = modules
|
total =
|
||||||
|> Enum.flat_map(&(&1.all_koans))
|
modules
|
||||||
|> Enum.count
|
|> Enum.flat_map(& &1.all_koans)
|
||||||
|
|> Enum.count()
|
||||||
|
|
||||||
Agent.update(__MODULE__, fn _ -> %Tracker{total: total} end)
|
Agent.update(__MODULE__, fn _ -> %Tracker{total: total} end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def completed(module, koan) do
|
def completed(module, koan) do
|
||||||
Agent.update(__MODULE__, &mark_koan_completed(&1, module, koan))
|
Agent.update(__MODULE__, &mark_koan_completed(&1, module, koan))
|
||||||
|
|
||||||
if complete?() do
|
if complete?() do
|
||||||
Agent.cast(__MODULE__, fn state ->
|
Agent.cast(__MODULE__, fn state ->
|
||||||
send(state.on_complete, {self(), :complete})
|
send(state.on_complete, {self(), :complete})
|
||||||
@@ -34,14 +36,18 @@ defmodule Tracker do
|
|||||||
|
|
||||||
def wait_until_complete() do
|
def wait_until_complete() do
|
||||||
pid = Process.whereis(Tracker)
|
pid = Process.whereis(Tracker)
|
||||||
|
|
||||||
receive do
|
receive do
|
||||||
{^pid, :complete} -> :ok
|
{^pid, :complete} -> :ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp mark_koan_completed(state, module, koan) do
|
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
|
end
|
||||||
|
|
||||||
def visited do
|
def visited do
|
||||||
@@ -54,7 +60,7 @@ defmodule Tracker do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def summarize do
|
def summarize do
|
||||||
state = Agent.get(__MODULE__, &(&1))
|
state = Agent.get(__MODULE__, & &1)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
total: state.total,
|
total: state.total,
|
||||||
|
@@ -2,7 +2,7 @@ defmodule Watcher do
|
|||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
def start_link() do
|
def start_link() do
|
||||||
GenServer.start_link(__MODULE__, [dirs: ["lib/koans"]])
|
GenServer.start_link(__MODULE__, dirs: ["lib/koans"])
|
||||||
end
|
end
|
||||||
|
|
||||||
def init(args) do
|
def init(args) do
|
||||||
@@ -11,10 +11,11 @@ defmodule Watcher do
|
|||||||
{:ok, %{watcher_pid: watcher_pid}}
|
{:ok, %{watcher_pid: watcher_pid}}
|
||||||
end
|
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
|
if Enum.member?(events, :modified) do
|
||||||
path |> normalize |> reload
|
path |> normalize |> reload
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply, state}
|
{:noreply, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -22,11 +23,11 @@ defmodule Watcher do
|
|||||||
if Path.extname(file) == ".ex" do
|
if Path.extname(file) == ".ex" do
|
||||||
try do
|
try do
|
||||||
file
|
file
|
||||||
|> Code.load_file
|
|> Code.load_file()
|
||||||
|> Enum.map(&(elem(&1, 0)))
|
|> Enum.map(&elem(&1, 0))
|
||||||
|> Enum.find(&Runner.koan?/1)
|
|> Enum.find(&Runner.koan?/1)
|
||||||
|> Runner.modules_to_run
|
|> Runner.modules_to_run()
|
||||||
|> Runner.run
|
|> Runner.run()
|
||||||
rescue
|
rescue
|
||||||
e -> Display.show_compile_error(e)
|
e -> Display.show_compile_error(e)
|
||||||
end
|
end
|
||||||
|
@@ -8,30 +8,32 @@ defmodule BlanksTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "Work with multiple different replacements" do
|
test "Work with multiple different replacements" do
|
||||||
[koan | _] = SampleKoan.all_koans
|
[koan | _] = SampleKoan.all_koans()
|
||||||
assert :ok == apply(SampleKoan, koan, [{:multiple, [3,4]}])
|
assert :ok == apply(SampleKoan, koan, [{:multiple, [3, 4]}])
|
||||||
end
|
end
|
||||||
|
|
||||||
test "complex example" do
|
test "complex example" do
|
||||||
ast = quote do: assert true == ___
|
ast = quote do: assert(true == ___)
|
||||||
|
|
||||||
assert Blanks.replace(ast, true) == quote(do: assert true == true)
|
assert Blanks.replace(ast, true) == quote(do: assert(true == true))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "multiple arguments" do
|
test "multiple arguments" do
|
||||||
ast = quote do: assert ___ == ___
|
ast = quote do: assert(___ == ___)
|
||||||
|
|
||||||
assert Blanks.replace(ast, [true, false]) == quote(do: assert true == false)
|
assert Blanks.replace(ast, [true, false]) == quote(do: assert(true == false))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "pins variables in assert_receive replacement" do
|
test "pins variables in assert_receive replacement" do
|
||||||
ast = quote do: assert_receive ___
|
ast = quote do: assert_receive(___)
|
||||||
assert Blanks.replace(ast, Macro.var(:answer, __MODULE__)) == quote(do: assert_receive ^answer)
|
|
||||||
|
assert Blanks.replace(ast, Macro.var(:answer, __MODULE__)) ==
|
||||||
|
quote(do: assert_receive(^answer))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not pin values in assert_receive replacement" do
|
test "does not pin values in assert_receive replacement" do
|
||||||
ast = quote do: assert_receive ___
|
ast = quote do: assert_receive(___)
|
||||||
assert Blanks.replace(ast, :lolwat) == quote(do: assert_receive :lolwat)
|
assert Blanks.replace(ast, :lolwat) == quote(do: assert_receive(:lolwat))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "counts simple blanks" do
|
test "counts simple blanks" do
|
||||||
@@ -41,41 +43,46 @@ defmodule BlanksTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "counts multiple blanks" do
|
test "counts multiple blanks" do
|
||||||
ast = quote do: assert ___ == ___
|
ast = quote do: assert(___ == ___)
|
||||||
|
|
||||||
assert Blanks.count(ast) == 2
|
assert Blanks.count(ast) == 2
|
||||||
end
|
end
|
||||||
|
|
||||||
test "replaces whole line containing blank" do
|
test "replaces whole line containing blank" do
|
||||||
ast = quote do
|
ast =
|
||||||
1 + 2
|
quote do
|
||||||
2 + ___
|
1 + 2
|
||||||
end
|
2 + ___
|
||||||
|
end
|
||||||
|
|
||||||
expected_result = quote do
|
expected_result =
|
||||||
1 + 2
|
quote do
|
||||||
true
|
1 + 2
|
||||||
end
|
true
|
||||||
|
end
|
||||||
|
|
||||||
actual_result = Blanks.replace_line(ast, fn(_) -> true end)
|
actual_result = Blanks.replace_line(ast, fn _ -> true end)
|
||||||
|
|
||||||
assert actual_result == expected_result
|
assert actual_result == expected_result
|
||||||
end
|
end
|
||||||
|
|
||||||
test "replacement fn can access line" do
|
test "replacement fn can access line" do
|
||||||
ast = quote do
|
ast =
|
||||||
1 + 2
|
quote do
|
||||||
2 + ___
|
1 + 2
|
||||||
end
|
2 + ___
|
||||||
|
end
|
||||||
|
|
||||||
expected_result = quote do
|
expected_result =
|
||||||
1 + 2
|
quote do
|
||||||
some_fun(2 + ___)
|
1 + 2
|
||||||
end
|
some_fun(2 + ___)
|
||||||
|
end
|
||||||
|
|
||||||
actual_result = Blanks.replace_line(ast, fn(line) ->
|
actual_result =
|
||||||
quote do: some_fun(unquote(line))
|
Blanks.replace_line(ast, fn line ->
|
||||||
end)
|
quote do: some_fun(unquote(line))
|
||||||
|
end)
|
||||||
|
|
||||||
assert actual_result == expected_result
|
assert actual_result == expected_result
|
||||||
end
|
end
|
||||||
|
@@ -18,39 +18,39 @@ defmodule FailureTests do
|
|||||||
error = error(%ExUnit.AssertionError{expr: quote(do: :lol == :wat), left: :lol, right: :wat})
|
error = error(%ExUnit.AssertionError{expr: quote(do: :lol == :wat), left: :lol, right: :wat})
|
||||||
|
|
||||||
assert Failure.format_failure(error) == """
|
assert Failure.format_failure(error) == """
|
||||||
Assertion failed in some_file.ex:42
|
Assertion failed in some_file.ex:42
|
||||||
:lol == :wat
|
:lol == :wat
|
||||||
|
|
||||||
left: :lol
|
left: :lol
|
||||||
right: :wat
|
right: :wat
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
test "match failure" do
|
test "match failure" do
|
||||||
error = error(%ExUnit.AssertionError{expr: quote(do: match?(:lol,:wat)), right: :wat})
|
error = error(%ExUnit.AssertionError{expr: quote(do: match?(:lol, :wat)), right: :wat})
|
||||||
|
|
||||||
assert Failure.format_failure(error) == """
|
assert Failure.format_failure(error) == """
|
||||||
Assertion failed in some_file.ex:42
|
Assertion failed in some_file.ex:42
|
||||||
match?(:lol, :wat)
|
match?(:lol, :wat)
|
||||||
|
|
||||||
value does not match: :wat
|
value does not match: :wat
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
test "only offending lines are displayed for errors" do
|
test "only offending lines are displayed for errors" do
|
||||||
[koan] = SingleArity.all_koans
|
[koan] = SingleArity.all_koans()
|
||||||
error = apply(SingleArity, koan, []) |> error()
|
error = apply(SingleArity, koan, []) |> error()
|
||||||
|
|
||||||
assert Failure.format_failure(error) == """
|
assert Failure.format_failure(error) == """
|
||||||
Assertion failed in some_file.ex:42\nmatch?(:foo, ___)
|
Assertion failed in some_file.ex:42\nmatch?(:foo, ___)
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
defp error(error) do
|
defp error(error) do
|
||||||
%{
|
%{
|
||||||
error: error,
|
error: error,
|
||||||
file: "some_file.ex",
|
file: "some_file.ex",
|
||||||
line: 42
|
line: 42
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -3,7 +3,7 @@ defmodule NotificationTest do
|
|||||||
alias Display.Notifications
|
alias Display.Notifications
|
||||||
|
|
||||||
test "shows possible koans when a koan can not be found" do
|
test "shows possible koans when a koan can not be found" do
|
||||||
message = Notifications.invalid_koan(SampleKoan, [PassingKoan])
|
message = Notifications.invalid_koan(SampleKoan, [PassingKoan])
|
||||||
assert message == "Did not find koan SampleKoan in PassingKoan"
|
assert message == "Did not find koan SampleKoan in PassingKoan"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -13,7 +13,6 @@ defmodule ProgressBarTest do
|
|||||||
assert bar == "|=======> | 3 of 12"
|
assert bar == "|=======> | 3 of 12"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
test "full bar" do
|
test "full bar" do
|
||||||
bar = ProgressBar.progress_bar(%{total: 12, current: 12})
|
bar = ProgressBar.progress_bar(%{total: 12, current: 12})
|
||||||
assert bar == "|=============================>| 12 of 12"
|
assert bar == "|=============================>| 12 of 12"
|
||||||
|
@@ -12,6 +12,6 @@ defmodule ExecuteTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "can access intro" do
|
test "can access intro" do
|
||||||
assert SampleKoan.intro == "There is something\n"
|
assert SampleKoan.intro() == "There is something\n"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -8,7 +8,7 @@ defmodule AgentTests do
|
|||||||
"Why hello",
|
"Why hello",
|
||||||
"HI THERE",
|
"HI THERE",
|
||||||
{:multiple, [["Milk"], ["Bread", "Milk"]]},
|
{:multiple, [["Milk"], ["Bread", "Milk"]]},
|
||||||
false,
|
false
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Agents, answers)
|
test_all(Agents, answers)
|
||||||
|
@@ -9,17 +9,17 @@ defmodule EnumTests do
|
|||||||
{:multiple, [true, false]},
|
{:multiple, [true, false]},
|
||||||
{:multiple, [true, false]},
|
{:multiple, [true, false]},
|
||||||
{:multiple, [true, false]},
|
{:multiple, [true, false]},
|
||||||
[10,20,30],
|
[10, 20, 30],
|
||||||
[1,3],
|
[1, 3],
|
||||||
[2],
|
[2],
|
||||||
[1,2,3],
|
[1, 2, 3],
|
||||||
[1,2,3,4,5],
|
[1, 2, 3, 4, 5],
|
||||||
[1,2,3],
|
[1, 2, 3],
|
||||||
[a: 1, b: 2, c: 3],
|
[a: 1, b: 2, c: 3],
|
||||||
2,
|
2,
|
||||||
nil,
|
nil,
|
||||||
:no_such_element,
|
:no_such_element,
|
||||||
6,
|
6
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Enums, answers)
|
test_all(Enums, answers)
|
||||||
|
@@ -10,7 +10,7 @@ defmodule EqualitiesTests do
|
|||||||
2,
|
2,
|
||||||
1,
|
1,
|
||||||
4,
|
4,
|
||||||
2,
|
2
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Equalities, answers)
|
test_all(Equalities, answers)
|
||||||
|
@@ -7,7 +7,7 @@ defmodule FunctionsTests do
|
|||||||
"Hello, World!",
|
"Hello, World!",
|
||||||
3,
|
3,
|
||||||
{:multiple, ["One and Two", "Only One"]},
|
{:multiple, ["One and Two", "Only One"]},
|
||||||
{:multiple, ["Hello Hello Hello Hello Hello ","Hello Hello "]},
|
{:multiple, ["Hello Hello Hello Hello Hello ", "Hello Hello "]},
|
||||||
{:multiple, [:entire_list, :single_thing]},
|
{:multiple, [:entire_list, :single_thing]},
|
||||||
{:multiple, ["10 is bigger than 5", "4 is not bigger than 27"]},
|
{:multiple, ["10 is bigger than 5", "4 is not bigger than 27"]},
|
||||||
{:multiple, ["The number was zero", "The number was 5"]},
|
{:multiple, ["The number was zero", "The number was 5"]},
|
||||||
@@ -18,7 +18,7 @@ defmodule FunctionsTests do
|
|||||||
100,
|
100,
|
||||||
1000,
|
1000,
|
||||||
"Full Name",
|
"Full Name",
|
||||||
{:multiple, ["GOOD", "good"]},
|
{:multiple, ["GOOD", "good"]}
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Functions, answers)
|
test_all(Functions, answers)
|
||||||
|
@@ -13,7 +13,7 @@ defmodule GenServersTests do
|
|||||||
{:error, "Incorrect password!"},
|
{:error, "Incorrect password!"},
|
||||||
"Congrats! Your process was successfully named.",
|
"Congrats! Your process was successfully named.",
|
||||||
{:ok, "Laptop unlocked!"},
|
{:ok, "Laptop unlocked!"},
|
||||||
{:multiple, ["Laptop unlocked!", "Incorrect password!", "Jack Sparrow"]},
|
{:multiple, ["Laptop unlocked!", "Incorrect password!", "Jack Sparrow"]}
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(GenServers, answers)
|
test_all(GenServers, answers)
|
||||||
|
@@ -8,7 +8,7 @@ defmodule KeywordListsTests do
|
|||||||
"bar",
|
"bar",
|
||||||
"baz",
|
"baz",
|
||||||
{:multiple, [:foo, "bar"]},
|
{:multiple, [:foo, "bar"]},
|
||||||
"foo",
|
"foo"
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(KeywordLists, answers)
|
test_all(KeywordLists, answers)
|
||||||
|
@@ -3,24 +3,25 @@ defmodule ListsTests do
|
|||||||
import TestHarness
|
import TestHarness
|
||||||
|
|
||||||
test "Lists" do
|
test "Lists" do
|
||||||
answers = [1,
|
answers = [
|
||||||
3,
|
1,
|
||||||
[1, 2, :a, "b"],
|
3,
|
||||||
[1,2],
|
[1, 2, :a, "b"],
|
||||||
[:a, :c],
|
[1, 2],
|
||||||
[:a, :b],
|
[:a, :c],
|
||||||
["life", "life", "life"],
|
[:a, :b],
|
||||||
[1, 2, 3, 4, 5],
|
["life", "life", "life"],
|
||||||
[1, 4, 2, 3],
|
[1, 2, 3, 4, 5],
|
||||||
[10, 2, 3],
|
[1, 4, 2, 3],
|
||||||
[1, 2, 3],
|
[10, 2, 3],
|
||||||
[1, 2, 3, 4],
|
[1, 2, 3],
|
||||||
[1, 2, 3, 4],
|
[1, 2, 3, 4],
|
||||||
{1, 2, 3},
|
[1, 2, 3, 4],
|
||||||
["value"],
|
{1, 2, 3},
|
||||||
[],
|
["value"],
|
||||||
["value"],
|
[],
|
||||||
]
|
["value"]
|
||||||
|
]
|
||||||
|
|
||||||
test_all(Lists, answers)
|
test_all(Lists, answers)
|
||||||
end
|
end
|
||||||
|
@@ -14,7 +14,7 @@ defmodule MapSetsTest do
|
|||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
7,
|
7,
|
||||||
[1, 2, 3, 4, 5],
|
[1, 2, 3, 4, 5]
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(MapSets, answers)
|
test_all(MapSets, answers)
|
||||||
|
@@ -13,7 +13,7 @@ defmodule MapsTests do
|
|||||||
false,
|
false,
|
||||||
%{:first_name => "Jon", :last_name => "Snow"},
|
%{:first_name => "Jon", :last_name => "Snow"},
|
||||||
{:ok, "Baratheon"},
|
{:ok, "Baratheon"},
|
||||||
%{:first_name => "Jon", :last_name => "Snow"},
|
%{:first_name => "Jon", :last_name => "Snow"}
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Maps, answers)
|
test_all(Maps, answers)
|
||||||
|
@@ -30,7 +30,7 @@ defmodule NumbersTests do
|
|||||||
{:multiple, [6.0, 5.0, 8.9, -5.567]},
|
{:multiple, [6.0, 5.0, 8.9, -5.567]},
|
||||||
{:multiple, [1, 10]},
|
{:multiple, [1, 10]},
|
||||||
{:multiple, [true, true, false]},
|
{:multiple, [true, true, false]},
|
||||||
{:multiple, [true, false]},
|
{:multiple, [true, false]}
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Numbers, answers)
|
test_all(Numbers, answers)
|
||||||
|
@@ -5,14 +5,14 @@ defmodule PatternsTests do
|
|||||||
test "Pattern Matching" do
|
test "Pattern Matching" do
|
||||||
answers = [
|
answers = [
|
||||||
1,
|
1,
|
||||||
{:multiple, [1, [2,3,4]]},
|
{:multiple, [1, [2, 3, 4]]},
|
||||||
[1,2,3,4],
|
[1, 2, 3, 4],
|
||||||
3,
|
3,
|
||||||
"eggs, milk",
|
"eggs, milk",
|
||||||
"Honda",
|
"Honda",
|
||||||
MatchError,
|
MatchError,
|
||||||
{:multiple, [:make, "Honda"]},
|
{:multiple, [:make, "Honda"]},
|
||||||
[1,2,3],
|
[1, 2, 3],
|
||||||
{:multiple, ["Meow", "Woof", "Eh?"]},
|
{:multiple, ["Meow", "Woof", "Eh?"]},
|
||||||
{:multiple, ["Mickey", "Donald", "I need a name!"]},
|
{:multiple, ["Mickey", "Donald", "I need a name!"]},
|
||||||
"dog",
|
"dog",
|
||||||
|
@@ -18,7 +18,7 @@ defmodule ProcessesTests do
|
|||||||
{:waited_too_long, "I am impatient"},
|
{:waited_too_long, "I am impatient"},
|
||||||
{:exited, :random_reason},
|
{:exited, :random_reason},
|
||||||
:normal,
|
:normal,
|
||||||
:normal,
|
:normal
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Processes, answers)
|
test_all(Processes, answers)
|
||||||
|
@@ -11,7 +11,7 @@ defmodule SigilsTests do
|
|||||||
~S(1 + 1 = #{1+1}),
|
~S(1 + 1 = #{1+1}),
|
||||||
["Hello", "world"],
|
["Hello", "world"],
|
||||||
["Hello", "123"],
|
["Hello", "123"],
|
||||||
["Hello", ~S(#{1+1})],
|
["Hello", ~S(#{1+1})]
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Sigils, answers)
|
test_all(Sigils, answers)
|
||||||
|
@@ -13,7 +13,7 @@ defmodule StringTests do
|
|||||||
"banana",
|
"banana",
|
||||||
"banana",
|
"banana",
|
||||||
"StringStringString",
|
"StringStringString",
|
||||||
"LISTEN",
|
"LISTEN"
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Strings, answers)
|
test_all(Strings, answers)
|
||||||
|
@@ -11,7 +11,7 @@ defmodule StructsTests do
|
|||||||
{:ok, 22},
|
{:ok, 22},
|
||||||
%Structs.Airline{plane: %Structs.Plane{maker: :airbus}, name: "Southwest"},
|
%Structs.Airline{plane: %Structs.Plane{maker: :airbus}, name: "Southwest"},
|
||||||
%Structs.Airline{plane: %Structs.Plane{maker: :boeing, passengers: 202}, name: "Southwest"},
|
%Structs.Airline{plane: %Structs.Plane{maker: :boeing, passengers: 202}, name: "Southwest"},
|
||||||
%{plane: %{maker: :cessna}, name: "Southwest"},
|
%{plane: %{maker: :cessna}, name: "Southwest"}
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Structs, answers)
|
test_all(Structs, answers)
|
||||||
|
@@ -9,8 +9,8 @@ defmodule TasksTests do
|
|||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
9,
|
9,
|
||||||
[1,4,9,16],
|
[1, 4, 9, 16]
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Tasks, answers)
|
test_all(Tasks, answers)
|
||||||
end
|
end
|
||||||
|
@@ -11,7 +11,7 @@ defmodule TupleTests do
|
|||||||
{:a, :new_thing, "hi"},
|
{:a, :new_thing, "hi"},
|
||||||
{"Huey", "Dewey", "Louie"},
|
{"Huey", "Dewey", "Louie"},
|
||||||
{:this, :is, :awesome},
|
{:this, :is, :awesome},
|
||||||
[:this, :can, :be, :a, :list],
|
[:this, :can, :be, :a, :list]
|
||||||
]
|
]
|
||||||
|
|
||||||
test_all(Tuples, answers)
|
test_all(Tuples, answers)
|
||||||
|
@@ -5,5 +5,4 @@ defmodule RunnerTest do
|
|||||||
path = "lib/koans/01_just_an_example.ex"
|
path = "lib/koans/01_just_an_example.ex"
|
||||||
assert Runner.path_to_number(path) == 1
|
assert Runner.path_to_number(path) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@@ -23,10 +23,10 @@ defmodule TestHarness do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp check_results(results) do
|
defp check_results(results) do
|
||||||
Enum.each(results, &(assert &1 == :passed))
|
Enum.each(results, &assert(&1 == :passed))
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_all(pairs, module) do
|
def run_all(pairs, module) do
|
||||||
Enum.map(pairs, fn ({koan, answer}) -> Execute.run_koan(module, koan, [answer]) end)
|
Enum.map(pairs, fn {koan, answer} -> Execute.run_koan(module, koan, [answer]) end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -5,24 +5,24 @@ defmodule TrackerTest do
|
|||||||
|
|
||||||
test "can start" do
|
test "can start" do
|
||||||
Tracker.set_total(@sample_modules)
|
Tracker.set_total(@sample_modules)
|
||||||
assert Tracker.summarize == %{total: 2, current: 0, visited_modules: []}
|
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.set_total(@sample_modules)
|
Tracker.set_total(@sample_modules)
|
||||||
Tracker.completed(SampleKoan, :"Hi there")
|
Tracker.completed(SampleKoan, :"Hi there")
|
||||||
assert Tracker.summarize == %{total: 2, current: 1, visited_modules: [SampleKoan]}
|
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.set_total(@sample_modules)
|
Tracker.set_total(@sample_modules)
|
||||||
Tracker.completed(SampleKoan, :"Hi there")
|
Tracker.completed(SampleKoan, :"Hi there")
|
||||||
Tracker.completed(SampleKoan, :"Hi there")
|
Tracker.completed(SampleKoan, :"Hi there")
|
||||||
assert Tracker.summarize == %{total: 2, current: 1, visited_modules: [SampleKoan]}
|
assert Tracker.summarize() == %{total: 2, current: 1, visited_modules: [SampleKoan]}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "knows when koans are not complete" do
|
test "knows when koans are not complete" do
|
||||||
Tracker.set_total(@sample_modules)
|
Tracker.set_total(@sample_modules)
|
||||||
refute Tracker.complete?
|
refute Tracker.complete?()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user