@@ -27,15 +27,14 @@ defmodule BlankAssertions do
|
||||
end
|
||||
end
|
||||
|
||||
defmacro assert_receive(expr) do
|
||||
if contains_blank?(expr) do
|
||||
code = Macro.escape(expr)
|
||||
quote do
|
||||
raise ExUnit.AssertionError, expr: unquote(code)
|
||||
end
|
||||
def assert_receive(expr) do
|
||||
if expr == :__ do
|
||||
raise ExUnit.AssertionError, expr: expr
|
||||
else
|
||||
quote do
|
||||
ExUnit.Assertions.assert_receive(unquote(expr), 100)
|
||||
receive do
|
||||
^expr -> true
|
||||
after
|
||||
100 -> flunk("No message matching #{expr} found in mailbox")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -48,10 +47,8 @@ defmodule BlankAssertions do
|
||||
ExUnit.Assertions.refute(value, opts)
|
||||
end
|
||||
|
||||
defmacro flunk(message \\ "Flunked!") do
|
||||
quote do
|
||||
assert false, message: unquote(message)
|
||||
end
|
||||
def flunk(message \\ "Flunked!") do
|
||||
ExUnit.Assertions.flunk(message)
|
||||
end
|
||||
|
||||
defp contains_blank?(expr) do
|
||||
|
||||
@@ -4,62 +4,62 @@ defmodule Maps do
|
||||
koan "Maps represent structured data, like a person" do
|
||||
assert %{ :name => "Jon",
|
||||
:last_name => "Snow",
|
||||
:age => 27 } == %{ :name => "Jon",
|
||||
:age => 27 } == %{ :name => :__,
|
||||
:last_name => "Snow",
|
||||
:age => 27 }
|
||||
end
|
||||
|
||||
koan "You can get all the keys from the map" do
|
||||
assert Map.keys(%{ :name => "Jon", :last_name => "Snow", :age => 27 }) == [:age, :last_name, :name]
|
||||
assert Map.keys(%{ :name => "Jon", :last_name => "Snow", :age => 27 }) == :__
|
||||
end
|
||||
|
||||
koan "Or you can also get all the values from it" do
|
||||
assert Map.values(%{ :name => "Jon", :last_name => "Snow", :age => 27 }) == [27, "Snow", "Jon"]
|
||||
assert Map.values(%{ :name => "Jon", :last_name => "Snow", :age => 27 }) == :__
|
||||
end
|
||||
|
||||
koan "Fetching a value returns a touple with ok when it exists..." do
|
||||
assert Map.fetch(%{ :name => "Jon", :last_name => "Snow", :age => 27 }, :age) == {:ok, 27}
|
||||
assert Map.fetch(%{ :name => "Jon", :last_name => "Snow", :age => 27 }, :age) == :__
|
||||
end
|
||||
|
||||
koan "...or the atom :error when it doesnt." do
|
||||
assert Map.fetch(%{ :name => "Jon", :last_name => "Snow", :age => 27 }, :family) == :error
|
||||
assert Map.fetch(%{ :name => "Jon", :last_name => "Snow", :age => 27 }, :family) == :__
|
||||
end
|
||||
|
||||
koan "Extending a map is a simple as put'ing in a new pair" do
|
||||
assert Map.put(%{ :name => "Jon", :last_name => "Snow"}, :age, 27) == %{ :name => "Jon", :last_name => "Snow", :age => 27 }
|
||||
assert Map.put(%{ :name => "Jon", :last_name => "Snow"}, :age, 27) == :__
|
||||
end
|
||||
|
||||
koan "Put can also overwrite existing values" do
|
||||
assert Map.put(%{ :name => "Jon", :last_name => "Snow", :age => 15}, :age, 27) == %{ :name => "Jon", :last_name => "Snow", :age => 27 }
|
||||
assert Map.put(%{ :name => "Jon", :last_name => "Snow", :age => 15}, :age, 27) == :__
|
||||
end
|
||||
|
||||
koan "Or you can use some syntactic sugar for exiting elements." do
|
||||
initial = %{ :name => "Jon", :last_name => "Snow", :age => 15}
|
||||
assert %{ initial | :age => 27 } == %{ :name => "Jon", :last_name => "Snow", :age => 27 }
|
||||
initial = %{ :name => "Jon", :last_name => "Snow", :age => 16}
|
||||
assert %{ initial | :age => 27 } == :__
|
||||
end
|
||||
|
||||
koan "Can remove pairs by key" do
|
||||
assert Map.delete(%{ :name => "Jon", :last_name => "Snow", :age => 15}, :age) == %{ :name => "Jon", :last_name => "Snow"}
|
||||
assert Map.delete(%{ :name => "Jon", :last_name => "Snow", :age => 15}, :age) == :__
|
||||
end
|
||||
|
||||
koan "If you have a list of pairs, you can turn them into a map" do
|
||||
assert Map.new( [{:name, "Jon"}, {:last_name, "Snow"}, {:age, 27}] ) == %{ :name => "Jon", :last_name => "Snow", :age => 27 }
|
||||
assert Map.new( [{:name, "Jon"}, {:last_name, "Snow"}, {:age, 27}] ) == :__
|
||||
end
|
||||
|
||||
koan "Going back to a list of pairs is just as easy." do
|
||||
assert Map.to_list(%{ :name => "Jon", :last_name => "Snow", :age => 27 }) == [age: 27, last_name: "Snow", name: "Jon" ]
|
||||
assert Map.to_list(%{ :name => "Jon", :last_name => "Snow", :age => 27 }) == :__
|
||||
end
|
||||
|
||||
koan "Can merge maps" do
|
||||
assert Map.merge(%{:name => "Jon"}, %{ :last_name => "Snow"}) == %{:name => "Jon", :last_name => "Snow"}
|
||||
assert Map.merge(%{:name => "Jon"}, %{ :last_name => "Snow"}) == :__
|
||||
end
|
||||
|
||||
koan "When merging, the last map wins" do
|
||||
assert Map.merge(%{:name => "Robert", :last_name => "Snow"}, %{ :last_name => "Baratheon"}) == %{:name => "Robert", :last_name => "Baratheon"}
|
||||
assert Map.merge(%{:name => "Robert", :last_name => "Snow"}, %{ :last_name => "Baratheon"}) == :__
|
||||
end
|
||||
|
||||
koan "You can also select sub-maps out of a larger map" do
|
||||
initial = %{ :name => "Jon", :last_name => "Snow", :age => 15}
|
||||
assert Map.take(initial, [:name, :last_name]) == %{ :name => "Jon", :last_name => "Snow"}
|
||||
assert Map.take(initial, [:name, :last_name]) == :__
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,38 +2,38 @@ defmodule Strings do
|
||||
use Koans
|
||||
|
||||
koan "Strings are there to represent text" do
|
||||
assert "hello" == "hello"
|
||||
assert "hello" == :__
|
||||
end
|
||||
|
||||
koan "They can be put together" do
|
||||
assert "hello world" == "hello " <> "world"
|
||||
assert "hello world" == :__ <> "world"
|
||||
end
|
||||
|
||||
koan "Or pulled apart when needed" do
|
||||
assert ["hello", "world"] == String.split("hello world")
|
||||
assert :__ == String.split("hello world")
|
||||
end
|
||||
|
||||
koan "Be careful, a message may be altered" do
|
||||
assert "An awful day" == String.replace("An incredible day", "incredible", "awful")
|
||||
assert :__ == String.replace("An incredible day", "incredible", "awful")
|
||||
end
|
||||
|
||||
koan "But strings never lie about themselves" do
|
||||
assert true == String.contains?("An incredible day", "incredible")
|
||||
assert true == String.contains?("An incredible day", :__)
|
||||
end
|
||||
|
||||
koan "Sometimes you want just the opposite of what is given" do
|
||||
assert "banana" == String.reverse("ananab")
|
||||
assert :__ == String.reverse("ananab")
|
||||
end
|
||||
|
||||
koan "Other times a little cleaning is in order" do
|
||||
assert "banana" == String.strip(" \n banana\n ")
|
||||
assert :__ == String.strip(" \n banana\n ")
|
||||
end
|
||||
|
||||
koan "Repetition is the mother of learning" do
|
||||
assert "StringStringString" == String.duplicate("String", 3)
|
||||
assert "StringStringString" == String.duplicate(:__, 3)
|
||||
end
|
||||
|
||||
koan "Strings can be louder when necessary" do
|
||||
assert "LISTEN" == String.upcase("listen")
|
||||
assert "LISTEN" == String.upcase(:__)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,12 +6,12 @@ defmodule Functions do
|
||||
end
|
||||
# A function starts with 'def', has a 'do-end' pair
|
||||
koan "it returns the last statement that was called" do
|
||||
assert inside() == :light
|
||||
assert inside() == :__
|
||||
end
|
||||
|
||||
def quick_inline_product(a, b), do: a * b
|
||||
koan "Short functions can be defined in a single line, but mind the comman and colon!" do
|
||||
assert quick_inline_product(2,3) == 6
|
||||
assert quick_inline_product(2,:__) == 6
|
||||
end
|
||||
|
||||
# A function can have an argument between parentheses, after the name
|
||||
@@ -23,9 +23,12 @@ defmodule Functions do
|
||||
end
|
||||
end
|
||||
|
||||
koan "Can return different things" do
|
||||
assert will_change(true) == :it_was_truthy
|
||||
assert will_change(false) == "It really wasn't"
|
||||
koan "Can return atoms..." do
|
||||
assert will_change(true) == :__
|
||||
end
|
||||
|
||||
koan "..or strings from the same function!" do
|
||||
assert will_change(false) == :__
|
||||
end
|
||||
|
||||
def repeat(times, message) do
|
||||
@@ -33,64 +36,73 @@ defmodule Functions do
|
||||
end
|
||||
|
||||
koan "Functions can have more than one arguement" do
|
||||
assert repeat(3, "Hello ") == "Hello Hello Hello "
|
||||
assert repeat(3, "Hello ") == :__
|
||||
end
|
||||
|
||||
def repeat_again(times \\ 3, message) do
|
||||
def repeat_again(times \\ 5, message) do
|
||||
String.duplicate(message, times)
|
||||
end
|
||||
|
||||
koan "But sometimes, you may want to default some arguments" do
|
||||
assert repeat_again("Hello ") == "Hello Hello Hello "
|
||||
assert repeat_again("Hello ") == :__
|
||||
end
|
||||
|
||||
def first(foo, bar), do: "#{foo} and #{bar}"
|
||||
def first(foo), do: "only #{foo}"
|
||||
koan "Functions are distinguished by name and arity - the number of arguments" do
|
||||
assert first("One", "Two") == "One and Two"
|
||||
assert first("One") == "only One"
|
||||
koan "functions with the same name are distinguished by arity" do
|
||||
assert first("One", "Two") == :__
|
||||
end
|
||||
koan "the name stays - the number of arguments varies" do
|
||||
assert first("One") == :__
|
||||
end
|
||||
|
||||
|
||||
def sum_up(thing) when is_list(thing), do: :entire_list
|
||||
def sum_up(_thing), do: :single_thing
|
||||
koan "You can 'guard' functions against their arguments" do
|
||||
assert sum_up([1,2,3]) == :entire_list
|
||||
assert sum_up(1) == :single_thing
|
||||
koan "You can 'guard' functions from their arguments" do
|
||||
assert sum_up([1,2,3]) == :__
|
||||
end
|
||||
koan "no guard means just that - anything" 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}"
|
||||
koan "You can also create intricate guards based on the values" do
|
||||
assert bigger(10, 5) == "10 is bigger than 5"
|
||||
assert bigger(4, 27) == "4 is not bigger than 27"
|
||||
koan "You can also create intricate guards based on the values..." do
|
||||
assert bigger(10, 5) == :__
|
||||
end
|
||||
koan "...to make the body of the function clearer" do
|
||||
assert bigger(4, 27) == :__
|
||||
end
|
||||
|
||||
def the_length(0), do: "It was zero"
|
||||
def the_length(number), do: "The length was #{number}"
|
||||
koan "You can also 'guard' with concrete values" do
|
||||
assert the_length(0) == "It was zero"
|
||||
assert the_length(5) == "The length was 5"
|
||||
assert the_length(0) == :__
|
||||
end
|
||||
koan "Or just let the argument roll" do
|
||||
assert the_length(5) == :__
|
||||
end
|
||||
|
||||
koan "You can also define inline functions and call them with .()" do
|
||||
multiply = fn (a,b) -> a * b end
|
||||
assert multiply.(2,3) == 6
|
||||
assert multiply.(2,3) == :__
|
||||
end
|
||||
|
||||
koan "You can even go shorter, by using &(..) and positional arguments" do
|
||||
multiply = &(&1 * &2)
|
||||
assert multiply.(2,3) == 6
|
||||
assert multiply.(2,3) == :__
|
||||
end
|
||||
|
||||
def two_arguments(_first, second), do: second
|
||||
koan "You can also show that certain arguments are ignored in the body by adding an underscore" do
|
||||
assert two_arguments(:hi_there, "the other one") == "the other one"
|
||||
assert two_arguments(:hi_there, "the other one") == :__
|
||||
end
|
||||
|
||||
def multiply_then_call(number, fun), do: fun.(number*5)
|
||||
def square(number), do: number * number
|
||||
koan "You can 'capture' functions if you want to pass them around as values" do
|
||||
assert multiply_then_call(2, &square/1) == 100
|
||||
assert multiply_then_call(2, &square/1) == :__
|
||||
end
|
||||
|
||||
koan "functions can be combined elegantly with the pipe operator" do
|
||||
@@ -99,6 +111,6 @@ defmodule Functions do
|
||||
|> Enum.map(&(String.capitalize(&1)))
|
||||
|> Enum.join(" ")
|
||||
|
||||
assert result == "Full Name"
|
||||
assert result == :__
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,121 +2,119 @@ defmodule Enums do
|
||||
use Koans
|
||||
|
||||
koan "knowing how many elements are in a list is important for book-keeping!" do
|
||||
assert Enum.count([1,2,3,4]) == 4
|
||||
assert Enum.count([1,2,3,4]) == :__
|
||||
end
|
||||
|
||||
koan "..the same applies for counting elements in a map..." do
|
||||
assert Enum.count(%{ :a => 1, :b => 2, :c => 3, :d => 4}) == 4
|
||||
assert Enum.count(%{ :a => 1, :b => 2, :c => 3, :d => 4}) == :__
|
||||
end
|
||||
|
||||
koan "..or a keyword list..." do
|
||||
assert Enum.count([a: 1, b: 2, c: 3, d: 4]) == 4
|
||||
assert Enum.count([a: 1, b: 2, c: 3, d: 4]) == :__
|
||||
end
|
||||
|
||||
koan "all? can tell you if all your element have something in common!" do
|
||||
assert Enum.all?([1,2,3], fn element -> element < 5 end)
|
||||
koan "Elements can have a lot in common" do
|
||||
assert Enum.all?([1,2,3], fn element -> element < 5 end) == :__
|
||||
end
|
||||
|
||||
koan "the key aspec is _all_ : if one if different, all? will let you know!" do
|
||||
refute Enum.all?([1, 2, 3], fn element -> element <= 2 end)
|
||||
koan "If one if different, all elements are not alike" do
|
||||
assert Enum.all?([1, 2, 3], fn element -> element <= 2 end) == :__
|
||||
end
|
||||
|
||||
koan "sometimes you you just want to know if there is _some_ element with a certain trait." do
|
||||
assert Enum.any?([1,2,3], fn element -> rem(element, 2) == 0 end)
|
||||
koan "sometimes you you just want to know if there are any elements with a certain trait" do
|
||||
assert Enum.any?([1,2,3], fn element -> rem(element, 2) == 0 end) == :__
|
||||
end
|
||||
|
||||
koan "if not a single element fits the bill, any? returns false" do
|
||||
refute Enum.any?([1,2,3], fn element -> rem(element, 5) == 0 end)
|
||||
assert Enum.any?([1,2,3], fn element -> rem(element, 5) == 0 end) == :__
|
||||
end
|
||||
|
||||
koan "Sometimes you just want to know if an element is part of the party" do
|
||||
assert Enum.member?([1,2,3], 1)
|
||||
assert Enum.member?([1,2,3], 1) == :__
|
||||
end
|
||||
|
||||
koan "And sometimes your invited guests don't show up and miss the party" do
|
||||
refute Enum.member?([1,2,3], 30)
|
||||
assert Enum.member?([1,2,3], 30) == :__
|
||||
end
|
||||
|
||||
koan "map converts each element of a list by running some function with it" do
|
||||
assert Enum.map([1,2,3], fn element -> element * 10 end) == [10,20,30]
|
||||
assert Enum.map([1,2,3], fn element -> element * 10 end) == :__
|
||||
end
|
||||
|
||||
koan "You can even return a list with entirely different types!" do
|
||||
assert Enum.map([1,2,3], fn element -> rem(element, 2) == 0 end) == [false, true, false]
|
||||
koan "You can even return a list with entirely different types" do
|
||||
assert Enum.map([1,2,3], fn element -> rem(element, 2) == 0 end) == :__
|
||||
end
|
||||
|
||||
koan "But keep in mind that the original list remains unchanged!" do
|
||||
koan "But keep in mind that the original list remains unchanged" do
|
||||
input = [1,2,3]
|
||||
assert Enum.map(input, fn element -> rem(element, 2) == 0 end) == [false, true, false]
|
||||
assert input == [1,2,3]
|
||||
assert input == :__
|
||||
end
|
||||
|
||||
koan "Filter allows you to only keep what you really care about." do
|
||||
assert Enum.filter([1,2,3], fn element -> rem(element, 2) == 1 end) == [1,3]
|
||||
koan "Filter allows you to only keep what you really care about" do
|
||||
assert Enum.filter([1,2,3], fn element -> rem(element, 2) == 1 end) == :__
|
||||
end
|
||||
|
||||
koan "Reject will help you throw out unwanted cruft." do
|
||||
assert Enum.reject([1,2,3], fn element -> rem(element, 2) == 1 end) == [2]
|
||||
assert Enum.reject([1,2,3], fn element -> rem(element, 2) == 1 end) == :__
|
||||
end
|
||||
|
||||
koan "You three there, follow me!" do
|
||||
assert Enum.take([1,2,3,4,5], 3) == [1,2,3]
|
||||
assert Enum.take([1,2,3,4,5], 3) == :__
|
||||
end
|
||||
|
||||
koan "...you can ask for a lot, but Enum won't hand you more than you give" do
|
||||
assert Enum.take([1,2,3,4,5], 10) == [1,2,3,4,5]
|
||||
koan "You can ask for a lot, but Enum won't hand you more than you give" do
|
||||
assert Enum.take([1,2,3,4,5], 10) == :__
|
||||
end
|
||||
|
||||
koan "Take what you can..." do
|
||||
assert Enum.take_while([1,2,3,4,5], fn element -> element < 4 end) == [1,2,3]
|
||||
assert Enum.take_while([1,2,3,4,5], fn element -> element < 4 end) == :__
|
||||
end
|
||||
|
||||
koan "Just like taking, you can also drop elements" do
|
||||
assert Enum.drop([-1,0,1,2,3], 2) == [1,2,3]
|
||||
assert Enum.drop([-1,0,1,2,3], 2) == :__
|
||||
end
|
||||
|
||||
koan "...and drop elements until you are happy." do
|
||||
assert Enum.drop_while([-1,0,1,2,3], fn element -> element <= 0 end) == [1,2,3]
|
||||
koan "Drop elements until you are happy" do
|
||||
assert Enum.drop_while([-1,0,1,2,3], fn element -> element <= 0 end) == :__
|
||||
end
|
||||
|
||||
koan "Forming groups makes uns stronger" do
|
||||
odd_or_even = fn element -> if rem(element, 2) == 0 do :even else :odd end end
|
||||
|
||||
assert Enum.group_by([1,2,3,4], odd_or_even) == %{ :odd => [3,1], :even => [4,2] }
|
||||
assert Enum.group_by([1,2,3,4], odd_or_even) == :__
|
||||
end
|
||||
|
||||
koan "You get as many groups as you can have different results" do
|
||||
assert Enum.group_by([1,2,3,4,5,6], fn element -> rem(element, 3) end) == %{ 0 => [6, 3], 1 => [4, 1], 2 => [5, 2]}
|
||||
assert Enum.group_by([1,2,3,4,5,6], fn element -> rem(element, 3) end) == :__
|
||||
|
||||
end
|
||||
|
||||
koan "Zip-up in pairs!" do
|
||||
numbers = [1,2,3]
|
||||
letters = [:a, :b, :c]
|
||||
assert Enum.zip(numbers, letters) == [{1, :a}, {2, :b}, {3, :c}]
|
||||
assert Enum.zip(numbers, letters) == :__
|
||||
end
|
||||
|
||||
koan "Sorry, but if you don't have a pair you are left out" do
|
||||
more_numbers = [1,2,3] ++ [4,5]
|
||||
letters = [:a, :b, :c]
|
||||
assert Enum.zip(more_numbers, letters) == [{1, :a}, {2, :b}, {3, :c}]
|
||||
assert Enum.zip(more_numbers, letters) == :__
|
||||
end
|
||||
|
||||
koan "When you want to find that one pesky element..." do
|
||||
assert Enum.find([1,2,3], fn element -> rem(element,2) == 0 end) == 2
|
||||
koan "When you want to find that one pesky element" do
|
||||
assert Enum.find([1,2,3], fn element -> rem(element,2) == 0 end) == :__
|
||||
end
|
||||
|
||||
koan "...but you don't quite find it..." do
|
||||
assert Enum.find([1,2,3], fn element -> rem(element,5) == 0 end) == nil
|
||||
assert Enum.find([1,2,3], fn element -> rem(element,5) == 0 end) == :__
|
||||
end
|
||||
|
||||
koan "...you can settle for a consolation prize" do
|
||||
assert Enum.find([1,2,3], :no_such_element, fn element -> rem(element,5) == 0 end) == :no_such_element
|
||||
assert Enum.find([1,2,3], :no_such_element, fn element -> rem(element,5) == 0 end) == :__
|
||||
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) == 6
|
||||
|
||||
assert Enum.reduce(["1", "2", "3"], "", fn(element, accumulator) -> element <> accumulator end) == "321"
|
||||
assert Enum.reduce([1,2,3], 0, fn(element, accumulator) -> element + accumulator end) == :__
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,53 +2,51 @@ defmodule Arithmetic do
|
||||
use Koans
|
||||
|
||||
koan "it can add numbers" do
|
||||
assert 3 + 4 == 7
|
||||
assert 3 + :__ == 7
|
||||
end
|
||||
|
||||
koan "it can substract numbers" do
|
||||
assert 7 - 4 == 3
|
||||
assert 7 - 4 == :__
|
||||
end
|
||||
|
||||
koan "it can multiply" do
|
||||
assert 3 * 4 == 12
|
||||
assert 3 * 4 == :__
|
||||
end
|
||||
|
||||
koan "floats and integers can be equal..." do
|
||||
assert 3.0 == 3
|
||||
assert 3.0 == :__
|
||||
end
|
||||
|
||||
koan "..unless they are not precisely the same" do
|
||||
assert 3.2 != 3
|
||||
assert 3.2 != :__
|
||||
end
|
||||
|
||||
koan "it does floating point division" do
|
||||
assert 5 / 2 == 2.5
|
||||
assert 5 / 2 == :__
|
||||
end
|
||||
|
||||
koan "it does integer division" do
|
||||
numerator = 5
|
||||
denominator = 2
|
||||
assert div(numerator, denominator) == 2
|
||||
assert div(numerator, denominator) == :__
|
||||
end
|
||||
|
||||
koan "it calculates the remainder of a division" do
|
||||
numerator = 5
|
||||
denominator = 2
|
||||
assert rem(numerator, denominator) == 1
|
||||
assert rem(numerator, denominator) == :__
|
||||
end
|
||||
|
||||
koan "it finds the maximum in a list" do
|
||||
assert Enum.max([1,2,3,4]) == 4
|
||||
end
|
||||
|
||||
koan "it can compare numbers" do
|
||||
assert 5 > 2
|
||||
assert 5 >= 5
|
||||
assert 3 < 7
|
||||
assert 5 <= 5
|
||||
assert Enum.max([1,2,3,4]) == :__
|
||||
end
|
||||
|
||||
koan "it finds the minimum in a list" do
|
||||
assert Enum.min([1,2,3,4]) == 1
|
||||
assert Enum.min([1,2,3,4]) == :__
|
||||
end
|
||||
|
||||
koan "it can compare numbers" do
|
||||
assert 5 > :__
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -7,36 +7,34 @@ defmodule Structs do
|
||||
|
||||
koan "structs are defined and named after a module" do
|
||||
person = %Person{}
|
||||
assert person == %Person{}
|
||||
assert person == :__
|
||||
end
|
||||
|
||||
koan "you can access the fields of a struct" do
|
||||
nobody = %Person{}
|
||||
assert nobody.age == nil
|
||||
assert nobody.name == nil
|
||||
assert nobody.age == :__
|
||||
end
|
||||
|
||||
koan "You can pass initial values to structs" do
|
||||
joe = %Person{name: "Joe", age: 23}
|
||||
assert joe.age == 23
|
||||
assert joe.name == "Joe"
|
||||
assert joe.name == :__
|
||||
end
|
||||
|
||||
koan "update fields with the pipe '|' operator" do
|
||||
joe = %Person{name: "Joe", age: 23}
|
||||
older = %{ joe | age: joe.age + 10}
|
||||
assert older.age == 33
|
||||
assert older.age == :__
|
||||
end
|
||||
|
||||
koan "the original struct is not affected by updates" do
|
||||
joe = %Person{name: "Joe", age: 23}
|
||||
assert %{ joe | age: joe.age + 10}.age == 33
|
||||
assert joe.age == 23
|
||||
assert joe.age == :__
|
||||
end
|
||||
|
||||
koan "you can pattern match into the fields of a struct" do
|
||||
%Person{age: age} = %Person{age: 22, name: "Silvia"}
|
||||
assert age == 22
|
||||
assert age == :__
|
||||
end
|
||||
|
||||
defmodule Plane do
|
||||
@@ -44,16 +42,14 @@ defmodule Structs do
|
||||
end
|
||||
|
||||
koan "or onto the type of the struct itself" do
|
||||
assert is_a_plane(%Plane{passengers: 417, maker: :boeing})
|
||||
refute is_a_plane(%Person{age: 22, name: "Silvia"})
|
||||
assert is_a_plane(%Plane{passengers: 417, maker: :boeing}) == :__
|
||||
end
|
||||
|
||||
def is_a_plane(%Plane{}), do: true
|
||||
def is_a_plane(_), do: false
|
||||
|
||||
koan "are basically maps" do
|
||||
silvia = %Person{age: 22, name: "Silvia"}
|
||||
|
||||
assert Map.fetch!(silvia, :age) == 22
|
||||
assert Map.fetch!(silvia, :age) == :__
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,56 +2,60 @@ defmodule PatternMatching do
|
||||
use Koans
|
||||
|
||||
koan "one matches one" do
|
||||
assert match?(1, 1)
|
||||
assert match?(1, :__)
|
||||
end
|
||||
|
||||
koan "a pattern can change" do
|
||||
a = 1
|
||||
assert a = 2
|
||||
assert a = :__
|
||||
end
|
||||
|
||||
# TODO not sure about this koan?
|
||||
koan "a pattern can also be strict" do
|
||||
a = 1
|
||||
assert ^a = 2
|
||||
assert ^a = :__
|
||||
end
|
||||
|
||||
koan "patterns can be used to pull things apart" do
|
||||
[head | tail] = [1,2,3,4]
|
||||
[head | _tail] = [1,2,3,4]
|
||||
|
||||
assert head == 1
|
||||
assert tail == [2,3,4]
|
||||
assert head == :__
|
||||
end
|
||||
|
||||
koan "or put them back together" do
|
||||
koan "...whichever side you actually need" do
|
||||
[_head | tail] = [1,2,3,4]
|
||||
|
||||
assert tail == :__
|
||||
end
|
||||
|
||||
|
||||
koan "and then put them back together" do
|
||||
head = 1
|
||||
tail = [2,3,4]
|
||||
|
||||
assert [1,2,3,4] == [head | tail]
|
||||
assert :__ == [head | tail]
|
||||
end
|
||||
|
||||
koan "Some values can be irrelevant" do
|
||||
[_first, _second, third, _fourth] = [1,2,3,4]
|
||||
|
||||
assert third == 3
|
||||
assert third == :__
|
||||
end
|
||||
|
||||
koan "strings come apart just a easily" do
|
||||
"Shopping list: " <> items = "Shopping list: eggs, milk"
|
||||
|
||||
assert items == "eggs, milk"
|
||||
assert items == :__
|
||||
end
|
||||
|
||||
koan "patterns show what you really care about" do
|
||||
%{make: make} = %{type: "car", year: 2016, make: "Honda", color: "black"}
|
||||
|
||||
assert make == "Honda"
|
||||
assert make == :__
|
||||
end
|
||||
|
||||
koan "the pattern can make assertions about what it expects" do
|
||||
the_list = [1, 2, 3]
|
||||
|
||||
assert match?([1, _, _], the_list)
|
||||
refute match?([2, _, _], the_list)
|
||||
assert match?([1, _, _], :__)
|
||||
end
|
||||
|
||||
def make_noise(%{type: "cat"}), do: "Meow"
|
||||
@@ -60,20 +64,26 @@ defmodule PatternMatching do
|
||||
|
||||
koan "functions can declare what kind of arguments they accept" do
|
||||
dog = %{type: "dog", legs: 4, age: 9, color: "brown"}
|
||||
cat = %{type: "cat", legs: 4, age: 3, color: "grey"}
|
||||
snake = %{type: "snake", legs: 0, age: 20, color: "black"}
|
||||
|
||||
assert make_noise(dog) == "Woof"
|
||||
assert make_noise(cat) == "Meow"
|
||||
assert make_noise(snake) == "Eh?"
|
||||
assert make_noise(dog) == :__
|
||||
end
|
||||
|
||||
koan "...and for cats..." do
|
||||
cat = %{type: "cat", legs: 4, age: 3, color: "grey"}
|
||||
assert make_noise(cat) == :__
|
||||
end
|
||||
|
||||
koan "...and for snakes..." do
|
||||
snake = %{type: "snake", legs: 0, age: 20, color: "black"}
|
||||
assert make_noise(snake) == :__
|
||||
end
|
||||
|
||||
koan "errors are shaped differently than sucessful results" do
|
||||
result = case Map.fetch(%{}, :obviously_not_a_key) do
|
||||
{:ok, val} -> val
|
||||
:error -> "not present"
|
||||
_ -> flunk("I should not happen")
|
||||
end
|
||||
|
||||
assert result == "not present"
|
||||
assert result == :__
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,18 +2,18 @@ defmodule Processes do
|
||||
use Koans
|
||||
|
||||
koan "tests run in a process!" do
|
||||
assert Process.alive?(self)
|
||||
assert Process.alive?(:__)
|
||||
end
|
||||
|
||||
koan "can spew out information about a process" do
|
||||
information = Process.info(self)
|
||||
|
||||
assert information[:status] == :running
|
||||
assert information[:status] == :__
|
||||
end
|
||||
|
||||
koan "process can send messages to itself" do
|
||||
send self(), "hola!"
|
||||
assert_receive "hola!"
|
||||
assert_receive :__
|
||||
end
|
||||
|
||||
koan "a common pattern is to include the sender in the message" do
|
||||
@@ -24,7 +24,7 @@ defmodule Processes do
|
||||
end)
|
||||
|
||||
send pid, {:hello, self()}
|
||||
assert_receive :how_are_you?
|
||||
assert_receive :__
|
||||
end
|
||||
|
||||
koan "you don't have to wait forever for messages" do
|
||||
@@ -36,22 +36,20 @@ defmodule Processes do
|
||||
end
|
||||
end)
|
||||
|
||||
assert_receive {:waited_too_long, "I am inpatient"}
|
||||
assert_receive :__
|
||||
end
|
||||
|
||||
koan "killing a process will terminate it" do
|
||||
pid = spawn(fn -> Process.exit(self(), :kill) end)
|
||||
:timer.sleep(500)
|
||||
refute Process.alive?(pid)
|
||||
assert Process.alive?(pid) == :__
|
||||
end
|
||||
|
||||
koan "killing a process kills it for good" do
|
||||
pid = spawn(fn -> receive do
|
||||
end
|
||||
end)
|
||||
pid = spawn(fn -> receive do end end)
|
||||
assert Process.alive?(pid)
|
||||
Process.exit(pid, :kill)
|
||||
refute Process.alive?(pid)
|
||||
assert Process.alive?(pid) == :__
|
||||
end
|
||||
|
||||
koan "can trap a signal in a child process" do
|
||||
@@ -66,8 +64,7 @@ defmodule Processes do
|
||||
wait()
|
||||
Process.exit(pid, :random_reason)
|
||||
|
||||
assert_receive {:exited, :random_reason}
|
||||
refute Process.alive?(pid)
|
||||
assert_receive :__
|
||||
end
|
||||
|
||||
koan "quitting normally has no effect" do
|
||||
@@ -75,7 +72,7 @@ defmodule Processes do
|
||||
end
|
||||
end)
|
||||
Process.exit(pid, :normal)
|
||||
assert Process.alive?(pid)
|
||||
assert Process.alive?(pid) == :__
|
||||
end
|
||||
|
||||
koan "quititing your own process normally does terminate it though" do
|
||||
@@ -84,10 +81,9 @@ defmodule Processes do
|
||||
end
|
||||
end)
|
||||
|
||||
assert Process.alive?(pid)
|
||||
send pid, :bye
|
||||
:timer.sleep(100)
|
||||
refute Process.alive?(pid)
|
||||
assert Process.alive?(pid) == :__
|
||||
end
|
||||
|
||||
koan "linked processes are informed about exit signals of children when trapping those signals" do
|
||||
@@ -100,7 +96,7 @@ defmodule Processes do
|
||||
end
|
||||
end)
|
||||
|
||||
assert_receive {:exited, :normal}
|
||||
assert_receive :__
|
||||
end
|
||||
|
||||
koan "monitoring processes are informed via messages without having trapping" do
|
||||
@@ -112,7 +108,7 @@ defmodule Processes do
|
||||
end
|
||||
end)
|
||||
|
||||
assert_receive {:exited, :normal}
|
||||
assert_receive :__
|
||||
end
|
||||
|
||||
def wait do
|
||||
|
||||
@@ -4,20 +4,19 @@ defmodule Tasks do
|
||||
koan "Tasks can be used for asynchronous computations with results" do
|
||||
task = Task.async(fn -> 3 *3 end)
|
||||
do_other_stuff()
|
||||
assert Task.await(task) + 1 == 10
|
||||
assert Task.await(task) + 1 == :__
|
||||
end
|
||||
|
||||
koan "if you don't need a result, use start_link/1" do
|
||||
{:ok, _pid} = Task.start_link(fn -> 1+1 end)
|
||||
end
|
||||
|
||||
koan "yield returns nil if the task isn't done yet" do
|
||||
koan "yield returns nothing if the task isn't done yet" do
|
||||
handle = Task.async(fn ->
|
||||
:timer.sleep(100)
|
||||
3 * 3
|
||||
end)
|
||||
assert Task.yield(handle, 10) == nil
|
||||
assert Task.await(handle) == 9
|
||||
assert Task.yield(handle, 10) == :__
|
||||
end
|
||||
|
||||
koan "tasks can be aborted with shutdown" do
|
||||
@@ -25,13 +24,13 @@ defmodule Tasks do
|
||||
:timer.sleep(100)
|
||||
3 * 3
|
||||
end)
|
||||
refute Task.shutdown(handle)
|
||||
assert Task.shutdown(handle) == :__
|
||||
end
|
||||
|
||||
koan "shutdown will give you an answer if it has it" do
|
||||
handle = Task.async(fn -> 3 * 3 end)
|
||||
:timer.sleep(10)
|
||||
assert Task.shutdown(handle) == {:ok, 9}
|
||||
assert Task.shutdown(handle) == {:ok, :__}
|
||||
end
|
||||
|
||||
koan "you can yield to multiple tasks at once and extract the results" do
|
||||
@@ -40,7 +39,7 @@ defmodule Tasks do
|
||||
|> Task.yield_many(100)
|
||||
|> Enum.map(fn({_task,{:ok, result}}) -> result end)
|
||||
|
||||
assert squares == [1,4,9,16]
|
||||
assert squares == :__
|
||||
end
|
||||
|
||||
def do_other_stuff do
|
||||
|
||||
35
lib/koans/12_tuples.ex
Normal file
35
lib/koans/12_tuples.ex
Normal file
@@ -0,0 +1,35 @@
|
||||
defmodule Tuples do
|
||||
use Koans
|
||||
|
||||
koan "tuples have a size" do
|
||||
assert tuple_size({:a, :b, :c}) == :__
|
||||
end
|
||||
|
||||
koan "tuples can contain different things" do
|
||||
assert {:a, 1, "hi"} == :__
|
||||
end
|
||||
|
||||
koan "you can pull out individual elements" do
|
||||
assert elem({:a, "hi"}, 1) == :__
|
||||
end
|
||||
|
||||
koan "you can change individual elements of a tuple" do
|
||||
assert put_elem({:a, "hi"}, 1, "bye") == :__
|
||||
end
|
||||
|
||||
koan "you can also simply extend a tuple with new stuff" do
|
||||
assert Tuple.insert_at({:a, "hi"}, 1, :new_thing) == :__
|
||||
end
|
||||
|
||||
koan "add things at the end" do
|
||||
assert Tuple.append({"Huey", "Dewey"}, "Louie") == :__
|
||||
end
|
||||
|
||||
koan "or also remove them" do
|
||||
assert Tuple.delete_at({:this, :is, :not, :awesome}, 2) == :__
|
||||
end
|
||||
|
||||
koan "turn it into a list in case you need it" do
|
||||
assert Tuple.to_list({:this, :can, :be, :a, :list}) == :__
|
||||
end
|
||||
end
|
||||
@@ -34,6 +34,204 @@ defmodule KoansHarnessTest do
|
||||
test_all(Lists, answers)
|
||||
end
|
||||
|
||||
test "Maps" do
|
||||
answers = [
|
||||
"Jon",
|
||||
[:age, :last_name, :name],
|
||||
[27, "Snow", "Jon"],
|
||||
{:ok, 27},
|
||||
:error,
|
||||
%{ :name => "Jon", :last_name => "Snow", :age => 27 },
|
||||
%{ :name => "Jon", :last_name => "Snow", :age => 27 },
|
||||
%{ :name => "Jon", :last_name => "Snow", :age => 27 },
|
||||
%{ :name => "Jon", :last_name => "Snow"},
|
||||
%{ :name => "Jon", :last_name => "Snow", :age => 27 },
|
||||
[age: 27, last_name: "Snow", name: "Jon" ],
|
||||
%{:name => "Jon", :last_name => "Snow"},
|
||||
%{:name => "Robert", :last_name => "Baratheon"},
|
||||
%{ :name => "Jon", :last_name => "Snow"}
|
||||
]
|
||||
|
||||
test_all(Maps, answers)
|
||||
end
|
||||
|
||||
test "String" do
|
||||
answers = [
|
||||
"hello",
|
||||
"hello ",
|
||||
["hello", "world"],
|
||||
"An awful day",
|
||||
"incredible",
|
||||
"banana",
|
||||
"banana",
|
||||
"String",
|
||||
"listen"
|
||||
]
|
||||
|
||||
test_all(Strings, answers)
|
||||
end
|
||||
|
||||
test "Functions" do
|
||||
answers = [
|
||||
:light,
|
||||
3,
|
||||
:it_was_truthy,
|
||||
"It really wasn't",
|
||||
"Hello Hello Hello ",
|
||||
"Hello Hello Hello Hello Hello ",
|
||||
"One and Two",
|
||||
"only One",
|
||||
:entire_list,
|
||||
:single_thing,
|
||||
"10 is bigger than 5",
|
||||
"4 is not bigger than 27",
|
||||
"It was zero",
|
||||
"The length was 5",
|
||||
6,
|
||||
6,
|
||||
"the other one",
|
||||
100,
|
||||
"Full Name"
|
||||
]
|
||||
|
||||
test_all(Functions, answers)
|
||||
end
|
||||
|
||||
test "Enum" do
|
||||
answers = [
|
||||
4,
|
||||
4,
|
||||
4,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
[10,20,30],
|
||||
[false, true, false],
|
||||
[1,2,3],
|
||||
[1,3],
|
||||
[2],
|
||||
[1,2,3],
|
||||
[1,2,3,4,5],
|
||||
[1,2,3],
|
||||
[1,2,3],
|
||||
[1,2,3],
|
||||
%{ :odd => [3,1], :even => [4,2] },
|
||||
%{ 0 => [6, 3], 1 => [4, 1], 2 => [5, 2]},
|
||||
[{1, :a}, {2, :b}, {3, :c}],
|
||||
[{1, :a}, {2, :b}, {3, :c}],
|
||||
2,
|
||||
nil,
|
||||
:no_such_element,
|
||||
6
|
||||
]
|
||||
|
||||
test_all(Enums, answers)
|
||||
end
|
||||
|
||||
test "Arithmetic" do
|
||||
answers = [
|
||||
4,
|
||||
3,
|
||||
12,
|
||||
3,
|
||||
3,
|
||||
2.5,
|
||||
2,
|
||||
1,
|
||||
4,
|
||||
1,
|
||||
2
|
||||
]
|
||||
|
||||
test_all(Arithmetic, answers)
|
||||
end
|
||||
|
||||
test "Structs" do
|
||||
answers = [
|
||||
%Structs.Person{},
|
||||
nil,
|
||||
"Joe",
|
||||
33,
|
||||
23,
|
||||
22,
|
||||
true,
|
||||
22,
|
||||
]
|
||||
|
||||
test_all(Structs, answers)
|
||||
end
|
||||
|
||||
test "Pattern Matching" do
|
||||
answers = [
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
[2,3,4],
|
||||
[1,2,3,4],
|
||||
3,
|
||||
"eggs, milk",
|
||||
"Honda",
|
||||
[1,2,3],
|
||||
"Woof",
|
||||
"Meow",
|
||||
"Eh?",
|
||||
"not present"
|
||||
]
|
||||
|
||||
test_all(PatternMatching, answers)
|
||||
end
|
||||
|
||||
test "Processes" do
|
||||
answers = [
|
||||
self,
|
||||
:running,
|
||||
"hola!",
|
||||
:how_are_you?,
|
||||
{:waited_too_long, "I am inpatient"},
|
||||
false,
|
||||
false,
|
||||
{:exited, :random_reason},
|
||||
true,
|
||||
false,
|
||||
{:exited, :normal},
|
||||
{:exited, :normal}
|
||||
]
|
||||
|
||||
test_all(Processes, answers)
|
||||
end
|
||||
|
||||
test "Tasks" do
|
||||
answers = [
|
||||
10,
|
||||
:todo,
|
||||
nil,
|
||||
nil,
|
||||
9,
|
||||
[1,4,9,16]
|
||||
]
|
||||
|
||||
test_all(Tasks, answers)
|
||||
end
|
||||
|
||||
test "Tuples" do
|
||||
answers = [
|
||||
3,
|
||||
{:a, 1, "hi"},
|
||||
"hi",
|
||||
{:a, "bye"},
|
||||
{:a, :new_thing, "hi"},
|
||||
{"Huey", "Dewey", "Louie"},
|
||||
{:this, :is, :awesome},
|
||||
[:this, :can, :be, :a, :list]
|
||||
]
|
||||
|
||||
test_all(Tuples, answers)
|
||||
end
|
||||
|
||||
def test_all(module, answers) do
|
||||
module.all_koans
|
||||
|> Enum.zip(answers)
|
||||
|
||||
Reference in New Issue
Block a user