Controller style & structure in Phoenix 1.3

A bit off-topic, sorry, but since with returns any unmatched expression anyway

iex(1)> with {:ok, "a"} <- {:error, "b"}, do: :ok # no need for `else` block
{:error, "b"}

your controller actions can be simplified a bit

def show(conn, %{"id" => id}) do
  document = DocManager.get_document!(id)
  # {:ok, user_id} returned if token is valid and carries the user_id,
  # otherwise {:error, "note authorized" } is returned.
  # Users are allowed to read their own docs and public docs
  with {:ok, user_id} <- Token.user_id_from_header(conn),
       true <- document.attributes["public"] or user_id == document.author_id,
       do: render(conn, "show.json", document: document)
end

def show_public(conn, %{"id" => id}) do
  document = DocManager.get_document!(id)
  if document.attributes["public"] do
    render(conn, "show.json", document: document)
  else
    {:error, "Cannot display document"}
  end
end

Assuming attributes is a map, I would also separate true <- document.attributes["public"] or user_id == document.author_id into helper functions for better errors like

defp public_or_author(%{attributes: %{"public" => true}})

defp public?(%{attributes: %{"public" => true}}, do: :yep
defp public?(%{attributes: %{"public" => false}}, do: {:error, :not_public}

defp author?(_id, %{author_id: _id}), do: :yep
defp author?(_id, %{author_id: _author_id}), do: {:error, :not_author}

so that the with block becomes

with {:ok, user_id} <- Token.user_id_from_header(conn),
     :yep <- public?(document),
     :yep <- author?(user_id, document),
     do: render(conn, "show.json", document: document)
2 Likes