Elixir + Phoenix, programando na velocidade da luz

Jul 17 (pt)
Hoje resolvi trazer alguns "pontos chave" sobre como podemos ser mais produtivos programando com Elixir e Phoenix, este post pode ser tanto para quem já tem alguma experiência com a linguagem quanto para quem está iniciando. Talvez você já conheça algumas dessas dicas, outras talvez descubra agora, mas o principal é compartilhar e aprender. vamos lá:

1. Entenda todos os detalhes de pattern matching, guards, keyword lists e structs.

Conhecendo-os temos um grande ganho em produtividade. Diria que 60% do código que lemos e escrevemos com a linguagem envolvem essas estruturas, vale a pena dedicar bastante tempo as explorando e estudando, dessa forma ganhamos muito mais "fluidez" no desenvolvimento das demandas do dia a dia.

Veja alguns exemplos de detalhes que podem nos pegar de surpresa em determinado momento do nosso desenvolvimento:

1.1 Pattern matching com structs funcionam dessa forma: 
iex(1)> %x{} = %MinhaStruct{}
%MinhaStruct{hello: nil}
iex(2)> x
MinhaStruct 

Porém, não conseguimos criar structs através de variáveis:
iex(12)> x = MinhaStruct
MinhaStruct
iex(13)> %x{}

** (CompileError) iex:13: expected struct name to be a compile time atom or alias, got: x

Caso isso seja necessário, devemos utilizar a função struct:
iex(13)> defmodule User do
...(13)> defstruct name: "john"
...(13)> end
iex(14)> x = User
iex(15)> struct(x)
%User{name: "john"}

1.2 Na definição de guards, não podemos utilizar funções definidas no módulo:
iex(4)> defmodule Teste do
...(4)> def maiorquequatro(x), do: x > 4
...(4)> def verifica(numero) when maiorquequatro(numero), do: IO.puts("é maior que quatro") 
...(4)> def verifica(_numero), do: IO.puts("não é maior que quatro")                             
...(4)> end
** (CompileError) iex:6: cannot find or invoke local maiorquequatro/1 inside guard. Only macros can be invoked in a guard and they must be defined before their invocation. Called as: maiorquequatro(numero)

Para utilizar funções customizadas em guards, utilizamos macros:
 iex(9)> defmodule Teste do                                    
...(9)> defmacro maiorquequatro(numero) do
...(9)> quote do: unquote(numero) > 4                   
...(9)> end
...(9)> def verifica(numero) when maiorquequatro(numero), do: IO.puts("é maior que quatro")
...(9)> def verifica(_numero), do: IO.puts("não é maior que quatro")
...(9)> end
iex(10)> Teste.verifica(2)
não é maior que quatro
:ok
iex(11)> Teste.verifica(5)
é maior que quatro
:ok

1.3 Pattern matching em keyword lists funcionam dessa forma: 
iex(4)> [{:name, nome} | _] = [name: "henrique", id: 3]
[name: "henrique", id: 3]
iex(5)> nome
"henrique"

Lembrando que keyword lists são um syntax sugar para uma lista de tuplas...
iex(7)> [{:name, nome}, {:id, 3}] == [name: "henrique", id: 3]
true

Se quisessemos extrair somente o nome, por exemplo, não poderiamos fazer dessa maneira:
iex(6)> [{:name, nome}] = [name: "henrique", id: 3]    
  ** (MatchError) no match of right hand side value: [name: "henrique", id: 3]

Aqui vão links de estudo para se aprofundar bastante sobre esses temas:
https://hexdocs.pm/elixir/Kernel.html#defstruct/1
https://hexdocs.pm/elixir/Kernel.html#struct/2
https://www.poeticoding.com/the-beauty-of-pattern-matching-in-elixir/
https://joyofelixir.com/6-pattern-matching
https://elixir-lang.org/getting-started/pattern-matching.html
https://elixir-lang.org/getting-started/keywords-and-maps.html

2. Escreva um alias (autocomplete) na sua IDE para o require IEx; IEx.pry por favor.


Passamos bastante tempo "debugando" código nas aplicações, em qualquer linguagem. Felizmente o Elixir conta com ferramentas muito boas para nos auxiliar. Uma das mais usadas requer o módulo IEx:
require IEx

Dessa forma podemos criar um breakpoint que irá abrir um terminal quando o código passar por essa linha:
def hello(), do: IEx.pry

Porém, é comum esquecermos o require IEx no início do arquivo, logo passamos a escrever:
require IEx; IEx.pry

Acredite em mim, com o tempo você vai ficar cansado de escrever require IEx; IEx.pry toda hora! Na minha IDE tenho um alias que funciona da seguinte maneira, quando escrevo "pry" e salvo o arquivo ou dou um espaço ela automaticamente completa com o resto.

Se a sua IDE de preferência for o VIM (assim como a minha), adicione esse trecho de configuração no seu ~/.vimrc: 
abbr pry require IEx; IEx.pry

Faça isso e seja feliz!

3. Rode testes com iex -S mix test --trace para não dar timeout na sessão do IEx 


Quando vamos debugar algum ponto do código por um tempo mais longo (utilizando o require IEx; IEx.pry com os testes), o ideal é usar o comando acima ou alterar o tempo limite da sessão, caso contrário sua sessão encerrará em 60 segundos.
** (ExUnit.TimeoutError) test timed out after 60000ms. You can change the timeout:

Inclusive, também seria interessante criar um alias para esse comando, no seu terminal (~/.bashrc)
alias mixtest="iex -S mix test --trace"


4. Domine a biblioteca Plug 


A biblioteca Plug é uma das principais "portas de entrada" para o Elixir na web, inclusive, o framework Phoenix foi construído baseado nela. Ter domínio e confiança em sua utilização nos trás mais segurança e assertividade nas decisões tomadas em projetos Phoenix.

links para estudo:
https://ieftimov.com/post/a-deeper-dive-in-elixir-plug/
https://elixirschool.com/pt/lessons/specifics/plug/
https://hexdocs.pm/plug/readme.html

5. Conheça essas dicas do IEx:


5.1 É possível escrever um arquivo de ajuda para iniciar automaticamente variáveis e tudo o que queremos quando abrirmos o terminal do IEx em um projeto. Basta criar um arquivo chamado .iex.exs na raiz do projeto, exemplo:
# Importe funções e módulos
import_if_available(MinhaApp.MeuModulo)

# De print em algo antes do terminal iniciar
IO.puts("hello world")

# Faça o bind de variáveis que ficarão disponíveis no terminal
value = 13

5.2 Existem diversas funções disponíveis no IEx que podem nos deixar mais produtivos, veja alguma delas:
• b/1            - prints callbacks info and docs for a given module
• c/1            - compiles a file into the current directory
• c/2            - compiles a file to the given path
• cd/1           - changes the current directory
• clear/0        - clears the screen
• exports/1      - shows all exports (functions + macros) in a module
• flush/0        - flushes all messages sent to the shell
• h/0            - prints this help message
• h/1            - prints help for the given module, function or macro
• i/0            - prints information about the last value
• i/1            - prints information about the given term
• ls/0           - lists the contents of the current directory
• ls/1           - lists the contents of the specified directory
• open/1         - opens the source for the given module or function in
your editor
• pid/1          - creates a PID from a string
• pid/3          - creates a PID with the 3 integer arguments passed
• ref/1          - creates a Reference from a string
• ref/4          - creates a Reference with the 4 integer arguments
passed
• pwd/0          - prints the current working directory
• r/1            - recompiles the given module's source file
• recompile/0    - recompiles the current project
• runtime_info/0 - prints runtime info (versions, memory usage, stats)
• v/0            - retrieves the last value from the history
• v/1            - retrieves the nth value from the history

5.3 Tab completa o nome de módulos, funções e também mostra os métodos quando finalizamos um nome com ".":
 iex(9)> Enum.
EmptyError           OutOfBoundsError     all?/1               
all?/2               any?/1               any?/2                   
...

Os exemplos foram retirados da documentação do IEx: https://hexdocs.pm/iex/IEx.html#module-the-iex-exs-file

6. Conheça e utilize os diversos plugins para Elixir e Phoenix na sua IDE


Não se limite ao básico! Os plugins existem para serem utilizados, abuse deles!

Se você é usuário do VSCode, este artigo tem dicas muito boas: https://thinkingelixir.com/elixir-in-vs-code/
Para o VIM, de uma lida aqui: https://medium.com/@siever/setup-vim-for-elixir-development-280a01150152
Para o Atom: https://atom.io/packages/atom-elixir

7. Seguindo as dicas acima e utilizando a biblioteca phoenix_up, chegaremos a "299.792.458 features" por segundo!:


phoenix_upadiciona mais generators para desenvolver projetos Phoenix, basicamente, com essa biblioteca não teremos mais o trabalho de escrever arquivo por arquivo e estruturas repetitivas como: "BlaController, BlaView, templates/bla, BlaControllerTest" e etc.. Nos meus projetos, eu sempre uso!

Concluindo..


Apesar de algumas dicas serem mais "práticas", podemos perceber que outras são pontos onde vale a pena reforçar o estudo! Particularmente, hoje me sinto bastante confortável e produtivo com Elixir e Phoenix, e isso foi graças a esse equilíbrio de buscar ferramentas que me auxiliassem e estudar a fundo pontos fundamentais!


Ei, o que achou desse artigo?

Compartilhe e dê sua opinião clicando em uma das redes abaixo:


Muito obrigado!