defmodulePrinterTestdouseExUnit.Case test "print/1"do assert Printer.print(1) =="Number: 1" assert Printer.print("Hello") =="Text: Hello" assert Printer.print(%{name: "iago"}) =="Map with name: iago"endend
Vamos rodar-lo:
mixtesttest/printer_test.exsCompiling1file (.ex)Generatedhello_worldappwarning:Printer.print/1isundefined (module Printerisnotavailableorisyettobedefined)Invalidcallfoundat3locations:test/printer_test.exs:5:PrinterTest."test print/1"/1test/printer_test.exs:6:PrinterTest."test print/1"/1test/printer_test.exs:7:PrinterTest."test print/1"/11) test print/1 (PrinterTest)test/printer_test.exs:4** (UndefinedFunctionError) functionPrinter.print/1 is undefined (modulePrinterisnotavailable)code:assertPrinter.print(1) =="Number: 1"stacktrace:Printer.print(1)test/printer_test.exs:5: (test)Finishedin0.02seconds (0.00s async,0.02ssync)1test,1failure
Recebemos relatório de erro por não possuir o módulo e função utilizados. Vamos cria-los. Para reoslver esse problema, poderiamos usar cond facilmente:
defmodulePrinterTestdouseExUnit.Case test "print/1"do assert Printer.print(2) =="Number 2 is even" assert Printer.print(1) =="Number 1 is odd" assert Printer.print("Hello") =="Text: Hello" assert Printer.print(%{name: "iago"}) =="Map with name: iago"endend
Vamos alterar nossa implementação, utilizando ainda o cond/2.
lib/printer.ex
defmodulePrinterdorequireIntegerdefprint(arg) doconddo is_integer(arg) ->if (Integer.is_even(arg) ==true) do"Number #{arg} is even"else"Number #{arg} is odd"endis_bitstring(arg)->"Text: #{arg}"is_map(arg)->"Map with name: #{arg.name}"true->"Noops"endendend
A leitura começou a ficar um pouco bagunçada, nao? Isso foi uma mudança pequena. Você pode me dizer que poderiamos usar uma função ali. E você está certo em relação a isso. Mas ao invez disso, porque não isolamos a função print/1 para cada tipo de dado de entrar? Podemos fazer isso utilizando clauses. Elas são definidas ao lado da definição da função iniciando com a palavra chave when e uma operação a seguir. Vamos la:
lib/printer.ex
defmodulePrinterdorequireIntegerdefprint(arg) whenis_integer(arg) doif (Integer.is_even(arg) ==true) do"Number #{arg} is even"else"Number #{arg} is odd"endenddefprint(arg) whenis_bitstring(arg), do: "Text: #{arg}"defprint(arg) whenis_map(arg), do: "Map with name: #{arg.name}"end
Não preciso falar o quanto a leitura melhorou nesse exemplo certo? Temos diversas outras vantagens como, facilidade de extração e adição de novos tipos. Você pode perceber que as funções são controladas pelas funções seguidas do when. É ali que os guards moram.