Current section

Files

Jump to
ja_serializer lib ja_serializer phoenix_view.ex
Raw

lib/ja_serializer/phoenix_view.ex

defmodule JaSerializer.PhoenixView do
@moduledoc """
Use in your Phoenix.View to render jsonapi.org spec json.
See JaSerializer.Serializer for documentation on defining your serializer.
## Usage example
defmodule PhoenixExample.ArticleView do
use PhoenixExample.Web, :view
use JaSerializer.PhoenixView # Or use in web/web.ex
attributes [:title]
has_many :comments,
serializer: PhoenixExample.CommentsView,
include: false,
identifiers: :when_included
end
defmodule PhoenixExample.ArticlesController do
use PhoenixExample.Web, :controller
def index(conn, _params) do
render conn, "index.json-api", data: Repo.all(Article)
end
def show(conn, %{"id" => id}) do
article = Repo.get(Article, id) |> Repo.preload([:comments])
render conn, "show.json-api", data: article,
opts: [include: "comments"]
end
def create(conn, %{"data" => %{"attributes" => attrs}}) do
changeset = Article.changeset(%Article{}, attrs)
case Repo.insert(changeset) do
{:ok, article} ->
conn
|> put_status(201)
|> render("show.json-api", data: article)
{:error, changeset} ->
conn
|> put_status(422)
|> render(:errors, data: changeset)
end
end
end
"""
@doc false
defmacro __using__(opts \\ []) do
quote do
use JaSerializer, unquote(opts)
def render("index.json-api", data) do
JaSerializer.PhoenixView.render(__MODULE__, data)
end
def render("show.json-api", data) do
JaSerializer.PhoenixView.render(__MODULE__, data)
end
def render("errors.json-api", data) do
JaSerializer.PhoenixView.render_errors(data)
end
# These will be deprecated in the future
def render("index.json", data) do
IO.write(
:stderr,
IO.ANSI.format([
:red,
:bright,
"warning: Please use index.json-api instead. This will stop working in a future version.\n"
])
)
JaSerializer.PhoenixView.render(__MODULE__, data)
end
def render("show.json", data) do
IO.write(
:stderr,
IO.ANSI.format([
:red,
:bright,
"warning: Please use show.json-api instead. This will stop working in a future version.\n"
])
)
JaSerializer.PhoenixView.render(__MODULE__, data)
end
def render("errors.json", data) do
IO.write(
:stderr,
IO.ANSI.format([
:red,
:bright,
"warning: Please use errors.json-api instead. This will stop working in a future version.\n"
])
)
JaSerializer.PhoenixView.render_errors(data)
end
end
end
@doc """
Extracts the data and opts from the keyword list passed to render and returns
result of formatting.
"""
def render(serializer, data) do
struct = find_struct(serializer, data)
JaSerializer.format(serializer, struct, data[:conn], data[:opts] || [])
end
@doc """
Extracts the errors and opts from the data passed to render and returns
result of formatting.
`data` is expected to be either an invalid `Ecto.Changeset` or preformatted
errors as described in `JaSerializer.ErrorSerializer`.
"""
def render_errors(data) do
errors = data[:data] || data[:errors]
errors
|> error_serializer
|> apply(:format, [errors, data[:conn], data[:opts]])
end
defp error_serializer(%{__struct__: Ecto.Changeset}) do
JaSerializer.EctoErrorSerializer
end
defp error_serializer(_) do
JaSerializer.ErrorSerializer
end
defp find_struct(serializer, data) do
singular = singular_type(serializer.type())
plural = plural_type(serializer.type())
deprecated_struct = data[:model] || data[singular] || data[plural]
cond do
data[:data] ->
data[:data]
deprecated_struct ->
IO.write(
:stderr,
IO.ANSI.format([
:red,
:bright,
"warning: Passing data via `:model`, `:#{plural}` or `:#{singular}`
atoms to JaSerializer.PhoenixView has be deprecated. Please use
`:data` instead. This will stop working in a future version.\n"
])
)
deprecated_struct
is_nil(data[:data]) ->
nil
end
end
defp singular_type(type) do
type
|> Inflex.singularize()
|> String.to_atom()
end
defp plural_type(type) do
type
|> Inflex.pluralize()
|> String.to_atom()
end
end