Skip to content

Instantly share code, notes, and snippets.

@reginadiana
Last active May 15, 2023 23:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save reginadiana/a9be5229a9aaa94bc4906eba78db0664 to your computer and use it in GitHub Desktop.
Save reginadiana/a9be5229a9aaa94bc4906eba78db0664 to your computer and use it in GitHub Desktop.
Elixir & Phoenix

Elixir

O Elixir é uma linguagem de programação funcional brasileira criada por José Valin que roda na máquina virtual do Erlang.

📚 Hex (dependencias)

📚 ExUnit (testes)

📚 IEx (terminal interativo)

Básico

  • String.length("diana")

  • Para remover caracteres especiais e espaços em branco de uma string, podemos usar String.trim/1

  • Todo valor é verdadeiro com exceção de false e nil

  • Átomos são equivalentes a simbolos :example

  • iex entra no modo interativo

  • No Elixir não existem classes ou heranças, assim como declarações sem retorno de valor. Tudo é feito em funções.

  • Para fazer um ternário: if condicao, do: alguma_coisa, else: se_nao_outra_coisa

  • Para pegar o primeiro elemento de um array, podemos usar tanto array |> elem(1), como [primeiro_elemento] = array

Pré-carregamento

Para poder acessar os registros associados que os macros belongs_to, has_many e has_one nos expoem, precisamos pré-carregar os esquemas associados.

Um exemplo é quando queremos acessar os atores de um filme:

movie = Repo.get(Movie, 1)

movie.actors
%Ecto.Association.NotLoaded<association :actors is not loaded>

Não podemos acessar esses atores associados, a menos que os pré-carreguemos. Existem algumas maneiras diferentes de pré-carregar registros com o Ecto, e uma delas é com o preload.


Phoenix

Phoenix é um framework escrito em Elixir para o desenvolvimento de aplicações WEB.

📚 Doc

PostgreeSQL é o banco de dados padrão do Phoenix. Para configurar um banco diferente, basta usar a flag --database. Também podemos usar --no-ecto para não configurar nenhum banco.

Comandos no console

mix ecto.create cria o banco

mix ecto.migrate roda as migrations

mix phx.routes lista todas as rotas geradas em lib/

mix phx.serve roda o servidor

mix phx.new <name_project> cria um novo projeto

mix deps.get instala as dependencias do projeto

MIX_ENV=test roda no ambiente de teste

Estrutura de Pastas

📁 build contém todos os artefatos para compilação.

📁 assets contém tudo o que é relacionado ao javascript e css.

📁 config/config.exs é o arquivo responsável por configurar a aplicação.

📁 deps é um diretorio com todas as dependencias listadas em mix.exs.

📁 lib é um diretório que vai cuidar do MVC.

📁 lib/name_app é um diretório responsável pela parte de model, ou seja, visa tratar das regras de negocio.

📁 lib/name_app_web é um diretório responsável pela parte de view e controller, ou seja, contém tudo o que é necessário para expor as informações.


O logger irá mostrar as mensagens apenas nos metodos configurados em config/.exs.

Os tokens podem ser gerados e verificados para serem usados nos channels e endpoints como dados identificadores. É recomendado que eles expirem em 1 dia e o atributo namespace deve ser o mesmo para a função que gera e verifica o token.

⚠️ Sempre que o phoenix retornar No function clause matching, quer dizer que não está sendo retornado a estrutura elixir{:ok, :valid}

A configuração pepiline :browser permite lidar com requisições html.

O método Repo.get(Model, id) busca, por padrão, a partir do id. Se quisermos buscar por outro parametro, temos que user Repo.get_by(Model, other_param: value).

O operador pipe |> passa o resultado de uma expressão para outra.

Podemos usar o |> json(%{:ok, :valid}) como uma simples resposta a uma requisição.

O i18n do Phoenix é feito através do gettext, que já vem por padrão.

@moduledoc serve para documentar o metodo defmodule SomeModule

@doc serve para documentar os métodos def SomeSimpleModule

aridade em linguagens funcionais correspondem ao número de argumentos que uma função recebe. Por isso, é possível declarar funções com mesmo nome, mas que recebem número de parametros diferentes como my_function/1 que recebe um argumento e my_function/2 que recebe dois argumentos.

execução paralela: significa que duas ou mais tarefas são livres para serem executadas ao mesmo tempo.

execução concorrente: significa que o processador está trocando o foco entre as tarefas de maneira muito rápida.


Manipulação de Arquivos

Os métodos utilizados com File podem ser usados com o bang (!), permitindo que a função seja executada e o retorno seja seu prório conteúdo. Já quando não a utilizamos, a função será executada e o retorno será o math {:ok, "return function content"} ou {:error, :reason}

File.cd/1 Define o diretório de trabalho

File.cd!/1 Define o diretório de trabalho, mas retorna File.Error caso algo de errado

File.cd!/2 Muda o diretório de trabalho, então deve receber o diretório atual e o novo

Para declarar uma constante, fazemos:

defmodule MyModule do
  @my_favorite_number 13
end

Uso do then

O then é algo nativo do elixir que serve para capturarmos o valor retornado na pipe, sem precisarmos adicionar uma variável e só assim manupulá-la, exemplo:

string # "  diana \n"
|> String.trim() # "diana"
|> String.first() # "d"
|> String.upcase() # "D"
|> then(& &1 <> ".") # &1 é referente ao "D", então podemos fazer um join, retornando "D."

Como dar rollback (ou, fiz merda na migration, e agora?)

Primeiro de tudo, saiba que se voce não rodou a migrate, basta excluir o arquivo que foi gerado pela migration e é isso :)

Para dar rollback na ultima migration, basta executar:

mix ecto.rollback

É possível ainda dar rollback em uma migration especifica:

mix ecto.rollback -v [timestamp]

Depois, exclua o arquivo da ultima migration e execute a migrate novamente com:

mix ecto.migrate

Caso tenha algum erro de migration ao rodar os testes, tente dar um reset nesse ambiente:

Ex: Postgrex.Error 42P07 (duplicate_table): relation “users” already exists

MIX_ENV=test mix ecto.reset 

Funções anonimas

Exemplos:

function_variable = fn param -> param + 1 end
function_variable.(1) # 2

variable = &(&1 + 1)
variable.(1) # 2

Chamando uma função anonima dentro de outra

recursive_function_variable = &(variable.(&1))
recursive_function_variable.(1) # 2

Outro caso em que faço isso:

  def secret_combine(secret_function1, secret_function2) do
    fn arg -> secret_function2.(secret_function1.(arg)) end
  end

Pegando o argumento da função anonima dentro de outra função

  def secret_add(secret) do
    # arg_func é o param da função add e secret é o param da função secret_add
    fn arg_func -> arg_func + secret end
  end
  
add = Secrets.secret_add(6)
add.(9) # 15

Uma forma de devolver essa função de maneira mais enchuta, seria:

  def secret_add(secret) do
    &(&1 + secret)
  end

Quando criamos um mapa com chaves duplicadas, será retornado uma das chaves e a duplicação será removida com o seu respectivo valor:

iex(4)> %{3 => :two, 3 => "four"}    
warning: key 3 will be overridden in map
  iex:4

%{3 => "four"}

Acessando mapas

Considere o seguinte mapa:

map = %{a: 1, b: 2}

Quando a chave/key existe

Map.fetch(map, :a) # {:ok, 1}

Map.get(map, :a) # 1

map.a # 1

map[:b] # 1

Quando a chave/key NÃO existe

Map.fetch(map, :not_existing) # {:error}

Map.get(map, :not_existing) # nil

map.not_existing # ** (KeyError) key :not_existing not found in: %{a: 1, b: 2}

map[:not_existing] # nil

Para o caso do get, podemos definir um valor padrão para caso uma chave não exista

Map.get(map, :not_existing, "Valor alternativo") # "Valor alternativo"

O uso de map[key] é recomendado quando o mapa é criado dinamicamente e portanto, pode conter qualquer chave, seja ela de qualquer tipo. Já o map.key é usando quando temos bem definido quais são as chaves, criadas por intermédido do defstruct por exemplo.

As chaves também podem ser usadas dentro de variáveis, mas ao serem usadas em patter matchers, devem usar um prefixo:

iex(23)> id = 123
123
iex(24)> %{id => 123} = %{id => 123, 2 => :two, 3 => :three}
** (CompileError) iex:24: cannot use variable id as map key inside a pattern. Map keys in patterns can only be literals (such as atoms, strings, tuples, and the like) or an existing variable matched with the pin operator (such as ^some_var)
    (stdlib 3.13.2) lists.erl:1267: :lists.foldl/3
iex(24)> %{^id => 123} = %{id => 123, 2 => :two, 3 => :three}
%{2 => :two, 3 => :three, 123 => 123}

Métodos do Map

Delete

map = %{one: 1, two: 2}

map |> Map.delete(:one) # %{two: 2}

map |> Map.delete(:not_exist) # %{one: 1, two: 2}

O método delete vai retornar o mapa com a chave-valor deletados quando a chave existir, e retornar o próprio mapa quando a chave passada não existir, mas repare que, de qualquer forma, o mapa só tem a chave-valor deletada dentro daquela interação, então para deixá-la de fato deletada precisamos adicioná-la a uma variável.

Drop

map = %{one: 1, tree: 3, two: 2}

map |> Map.drop([:two, :tree]) # %{one: 1}

map |> Map.drop([:two, :four]) # %{one: 1, tree: 3}

map |> Map.drop([]) # %{one: 1, tree: 3, two: 2}

O método drop vai eliminar uma série de chaves-valores de uma mapa. Na primeira interação as chaves :two e :four foram passadas, então elas foram deletadas, restando apenas a chave :one. Na segunda interação as chaves :two e :four foram passadas, porém a chave :four não existe, fazendo com que apenas :two fosse deletada, restanto as chaves :one e :tree. Já na terceira interação, nenhuma chave foi passada, fazendo com que não houvesse alteração.

Equal

map_a = %{a: 1, b: 2}

map_b = %{a: 1, b: 2}

map_c = %{c: 1, b: 2}

map_a == map_b # true

map_a == map_c # false

Map.equal?(map_a, map_b) # true

Map.equal?(map_a, map_c) # false

O método equal? verifica se os 2 mapas passados possuem as mesmas chaves com os mesmos valores. Porém, o resultado é similar ao usar ==. Na verdade, existe uma diferença entre usar essas duas abordagens. Veja o caso de quando os valores possuem tipos diferentes:

Map.equal?(%{a: 1.0}, %{a: 1}) # false

%{a: 1.0} === %{a: 1} # false

%{a: 1.0} == %{a: 1} # true
@reginadiana
Copy link
Author

MIX_ENV=test mix ecto.reset

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment