diff --git a/lib/ast_mangler.ex b/lib/ast_mangler.ex deleted file mode 100644 index eed4281..0000000 --- a/lib/ast_mangler.ex +++ /dev/null @@ -1,8 +0,0 @@ -defmodule ASTMangler do - def expand(ast, replacement) do - Macro.prewalk(ast, fn(node) -> update(node, replacement) end) - end - - def update(:__, replacement), do: replacement - def update(node, _), do: node -end diff --git a/lib/blanks.ex b/lib/blanks.ex new file mode 100644 index 0000000..7ed8614 --- /dev/null +++ b/lib/blanks.ex @@ -0,0 +1,21 @@ +defmodule Blanks do + def replace(ast, replacement) when not is_list(replacement), do: replace(ast, [replacement]) + def replace([do: ast], replacements), do: [do: replace(ast, replacements)] + def replace(ast, replacements) do + ast + |> Macro.prewalk(replacements, &pre/2) + |> elem(0) + end + + defp pre(:__, [first | remainder]), do: {first, remainder} + defp pre(node, acc), do: {node, acc} + + def count(ast) do + ast + |> Macro.prewalk(0, &count/2) + |> elem(1) + end + + defp count(:__, acc), do: {node, acc+1} + defp count(node, acc), do: {node, acc} +end diff --git a/lib/koans.ex b/lib/koans.ex index c313581..e4c26c6 100644 --- a/lib/koans.ex +++ b/lib/koans.ex @@ -1,9 +1,12 @@ defmodule Koans do defmacro koan(name, body) do compiled_name = String.to_atom(name) - mangled_body = ASTMangler.expand(body, quote do: answer) + number_of_args = Blanks.count(body) quote do @koans unquote(compiled_name) + + generate_test_method(unquote(compiled_name), unquote(number_of_args), unquote(body)) + def unquote(compiled_name)() do try do unquote(body) @@ -12,13 +15,35 @@ defmodule Koans do e in _ -> e end end + end + end - def unquote(compiled_name)(answer) do - unquote(mangled_body) + 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, Koans)) + quote do + def unquote(name)(answer) do + converted = {answer} + unquote(single_var) :ok end end end + defmacro generate_test_method(name, number_of_args, body) do + answer_placeholders = create_vars(number_of_args) + multi_var = Blanks.replace(body, answer_placeholders) + quote do + def unquote(name)({:multiple, answers}) do + converted = List.to_tuple(answers) + unquote(multi_var) + :ok + end + end + end + + defp create_vars(amount) do + Enum.map(0..amount, fn (idx) -> quote do: elem(converted, unquote(idx)) end) + end defmacro __using__(_opts) do quote do diff --git a/lib/koans/12_task.ex b/lib/koans/12_task.ex index bdfcfb1..26970ee 100644 --- a/lib/koans/12_task.ex +++ b/lib/koans/12_task.ex @@ -8,7 +8,8 @@ defmodule Tasks do end koan "if you don't need a result, use start_link/1" do - {:ok, _pid} = Task.start_link(fn -> 1+1 end) + {result, _pid} = Task.start_link(fn -> 1+1 end) + assert result == :__ end koan "yield returns nothing if the task isn't done yet" do diff --git a/test/ast_mangler_test.exs b/test/ast_mangler_test.exs deleted file mode 100644 index 9cb66b7..0000000 --- a/test/ast_mangler_test.exs +++ /dev/null @@ -1,31 +0,0 @@ -defmodule ASTManglerTest do - use ExUnit.Case, async: true - - test "simple replacement" do - ast = quote do: 1 + :__ - - mangled = ASTMangler.expand(ast, 37) - assert {:+, [context: ASTManglerTest, import: Kernel], [1, 37]} == mangled - end - - def complex_example do - [head | tail] = [1,2,3,4] - - assert head == 1 - assert tail == [2,3,4] - end - - def foo(answer) do - if answer == :yes do - :bar - else - :batz - end - end - - test "complex example" do - ast = [do: {:assert, [line: 5], [{:==, [line: 5], [true, :__]}]}] - - assert [do: {:assert, [line: 5], [{:==, [line: 5], [true, true]}]}] == ASTMangler.expand(ast, true) - end -end diff --git a/test/blanks_test.exs b/test/blanks_test.exs new file mode 100644 index 0000000..9423e70 --- /dev/null +++ b/test/blanks_test.exs @@ -0,0 +1,54 @@ +defmodule BlanksTest do + use ExUnit.Case, async: true + + test "simple replacement" do + ast = quote do: 1 + :__ + + mangled = Blanks.replace(ast, 37) + assert {:+, [context: BlanksTest, import: Kernel], [1, 37]} == mangled + end + + test "Work with multiple different replacements" do + [koan | _] = SampleKoan.all_koans + assert :ok == apply(SampleKoan, koan, [{:multiple, [3,4]}]) + end + + def complex_example do + [head | tail] = [1,2,3,4] + + assert head == 1 + assert tail == [2,3,4] + end + + def foo(answer) do + if answer == :yes do + :bar + else + :batz + end + end + + test "complex example" do + ast = [do: {:assert, [line: 5], [{:==, [line: 5], [true, :__]}]}] + + assert [do: {:assert, [line: 5], [{:==, [line: 5], [true, true]}]}] == Blanks.replace(ast, true) + end + + test "multiple arguments" do + ast = [do: {:assert, [line: 5], [{:==, [line: 5], [:__, :__]}]}] + + assert [do: {:assert, [line: 5], [{:==, [line: 5], [true, false]}]}] == Blanks.replace(ast, [true, false]) + end + + test "counts simple blanks" do + ast = quote do: 1 + :__ + + assert Blanks.count(ast) == 1 + end + + test "counts multiple blanks" do + ast = [do: {:assert, [line: 5], [{:==, [line: 5], [:__, :__]}]}] + + assert Blanks.count(ast) == 2 + end +end diff --git a/test/koans_harness_test.exs b/test/koans_harness_test.exs index 5db07bb..c86ee4d 100644 --- a/test/koans_harness_test.exs +++ b/test/koans_harness_test.exs @@ -222,7 +222,7 @@ defmodule KoansHarnessTest do test "Tasks" do answers = [ 10, - :todo, + :ok, nil, nil, 9, diff --git a/test/sample_koan_test.exs b/test/sample_koan_test.exs new file mode 100644 index 0000000..d48e340 --- /dev/null +++ b/test/sample_koan_test.exs @@ -0,0 +1,8 @@ +defmodule SampleKoan do + use Koans + + koan "thinking more than once" do + assert 3 == :__ + assert 4 == :__ + end +end