Notes

Commands

Create new API project:

$ mix phx.new project-name --no-html --no-webpack

Create database:

with database online

$ mix ecto.create

Install dependencies:

$ mix deps.get

Create config file for credo:

$ mix credo.gen.config

Create a migration

$ mix ecto.gen.migration migration_name

Run migrations

$ mix ecto.migrate

Elixir

Manipulate maps

map = %{a: 1, b: 2}
IO.puts map[:a]
 
map2 = %{"a" => 1, "b" => 1}
IO.puts map["a"]

we can use Map.get/2 to get the value

iex> map = %{"a" => 1, "b" => 1}
iex> Map.get(map, "a")
1

we can use Map.get/3 to get the value of a index, or a default value

iex> map = %{"a" => 1, "b" => 1}
iex> map["name"]
nil
iex> Map.get(map, "name")
nil
iex> Map.get(map, "name", 5)
5

Phoenix

Use UUID as primary key

On config/config.exs add the config:

config :project, Project.Repo,
  migration_primary_key: [type: :binary_id],
  migration_foreign_key: [type: :binary_id]

Use resources to create default routes of a controller

On lib/project_web/router.ex:

scope "/api", ProjectWeb do
  pipe_through :api
 
  resources "/some-route", SomeController
end

This will create all default route for SomeController, running mix phx.routes we will get:

some_path  GET     /api/some-route           ProjectWeb.SomeController :index
some_path  GET     /api/some-route/:id/edit  ProjectWeb.SomeController :edit
some_path  GET     /api/some-route/new       ProjectWeb.SomeController :new
some_path  GET     /api/some-route/:id       ProjectWeb.SomeController :show
some_path  POST    /api/some-route           ProjectWeb.SomeController :create
some_path  PATCH   /api/some-route/:id       ProjectWeb.SomeController :update
           PUT     /api/some-route/:id       ProjectWeb.SomeController :update
some_path  DELETE  /api/some-route/:id       ProjectWeb.SomeController :delete

We can pass the parameter only to specify the methods:

scope "/api", ProjectWeb do
  pipe_through :api
 
  resources "/some-route", SomeController, only: [:create, :show]
end

Bamboo

First add bamboo to deps on mix.ex:

defp deps do
  [
    {:bamboo, "~> 2.1.0"}
  ]
end

Use Local Adapter

On config/config.exs add:

config :inmana, Inmana.Mailer, adapter: Bamboo.LocalAdapter

to use in teste, add the config on config/test.exs:

config :inmana, Inmana.Mailer, adapter: Bamboo.TestAdapter

Use dev router to see sent emails

On lib/project_web/router.ex:

if Mix.env() == :dev do
  forward "/sent_emails", Bamboo.SentEmailViewerPlug
end

GenServer

save code

defmodule Inmana.Supplies.Scheduler do
  use GenServer
 
  def init(state \\ %{}) do
    {:ok, state}
  end
 
  # async
  def handle_cast({:put, key, value}, state) do
    {:noreply, Map.put(state, key, value)}
  end
 
  # sync
  def handle_call({:get, key}, _from, state) do
    {:reply, Map.get(state, key), state}
  end
end

Tests

Common tests

use ExUnit.Case
 
describe "function/1" do
  test "description for this test" do
    params = "The params for the function"
    expected_result = "Expected result"
 
    result = Module.function(params)
 
    assert result == expected_result
  end
end

Changeset tests

use Project.DataCase
 
alias Ecto.Changeset
 
describe "changeset/1" do
  test "when all params are valid, return a valid changeset" do
    params = %{name: "Siri cascudo", email: "[email protected]"}
 
    response = Schema.changeset(params)
 
    assert %Changeset{
    changes: %{
      name: "Siri cascudo",
      email: "[email protected]"
    },
      valid?: true
    } = response
  end
 
  test "when there are invalid params, returns a invalid changeset" do
    params = %{name: "S", email: ""}
 
    expected_response = %{
      email: ["can't be blank"],
      name: ["should be at least 2 character(s)"]
    }
 
    response = Schema.changeset(params)
 
    assert %Changeset{valid?: false} = response
    assert errors_on(response) == expected_response
  end
end

Controller tests

use ProjectWeb.ConnCase
 
describe "create/2" do
  test "when all params are valid, create user", %{conn: conn} do
    params = %{name: "Siri cascudo", email: "[email protected]"}
 
    response =
      conn
      |> post(Routes.controller_path(conn, :create, params))
      |> json_response(:created)
 
    assert %{
      "message" => "User Created!",
      "user" => %{
         "email" => "[email protected]",
        "id" => _id,
        "name" => "Siri cascudo"
      }
    } = response
  end
 
  test "when there are invalid params, return an error", %{conn: conn} do
    params = %{email: "[email protected]"}
    expected_response = %{"message" => %{"name" => ["can't be blank"]}}
 
    response =
      conn
      |> post(Routes.controller_path(conn, :create, params))
      |> json_response(:bad_request)
 
    assert response == expected_response
  end
end

View tests

use ProjectWeb.ConnCase
 
import Phoenix.View
 
alias ProjectWeb.UserView
 
describe "render/2" do
  test "renders create.json" do
    params = %{name: "Siri cascudo", email: "[email protected]"}
    {:ok, restaurant} = Project.create_user(params)
 
    response = render(UserView, "create.json", user: user)
 
    assert %{
      message: "User Created!",
      user: %User{
        email: "[email protected]",
        id: _id,
        name: "Siri cascudo"
      }
    } = response
  end
end
 

referencies

Rocketseat: https://rocketseat.com.br/