Current section

10 Versions

Jump to

Compare versions

8 files changed
+65 additions
-54 deletions
  @@ -5,11 +5,11 @@ DRY up your Phoenix controllers.
5 5 ## Features
6 6
7 7 - Turns controllers into declarative code.
8 - - Works with any context function that returns a tagged tuple like `{:ok, ...}` or `{:error, ...}`
9 - - Understands `Ecto.Changeset` errors and can render the error.
10 - - Built in responders for JSON and HTML and ability to create custom ones.
8 + - Works with all context functions that return a tagged tuple like `{:ok, ...}` or `{:error, ...}`
9 + - Supports rendering `Ecto.Changeset` errors.
10 + - Built-in formatters for JSON and HTML with ability to add custom ones.
11 11
12 - **Disclaimer**: this is intended for simple apps or for when you're just starting out building an app. More complex apps will require more code in their controllers and should use standard Phoenix controllers and tests. This is just a starting point.
12 + **Disclaimer**: This is intended for simple apps or when starting to build an application. More complex apps may require more complexity in their controllers, and then a standard Phoenix controller with tests would work better. This is just a starting point.
13 13
14 14 ## Installation
15 15
  @@ -28,8 +28,7 @@ Add `use Transponder` to your controllers, and define actions with `defaction`:
28 28 ```elixir
29 29 defmodule MyAppWeb.Admin.ProductsController do
30 30 use MyAppWeb, :controller
31 - use Transponder,
32 - responder: Transponder.Responders.JSON
31 + use Transponder, format: Transponder.JSON
33 32
34 33 defaction :index, &Catalog.list_products(&1.params)
35 34 defaction :show, &Catalog.get_product(&1.params)
  @@ -45,11 +44,11 @@ Then define a `show.json.eex` and `index.json.eex`. Or you can use a `Phoenix.Vi
45 44 defmodule MyAppWeb.Admin.ProductsView do
46 45 use MyAppWeb, :view
47 46
48 - def render("index.json", %{response: products}) do
47 + def render("index.json", %{records: products}) do
49 48 %{products: Enum.map(&render("show.json", response: &1))}
50 49 end
51 50
52 - def render("show.json", %{response: product}) do
51 + def render("show.json", %{record: product}) do
53 52 %{
54 53 id: product.id,
55 54 title: product.title,
  @@ -59,13 +58,18 @@ defmodule MyAppWeb.Admin.ProductsView do
59 58 end
60 59 ```
61 60
62 - To render HTML instead of JSON use `Responders.HTML`:
61 + To handle errors, configure the error view in `config/config.exs`:
62 +
63 + ```elixir
64 + config :transponder, :error_view, MyAppWeb.ErrorView
65 + ```
66 +
67 + To render HTML instead of JSON use `Transponder.HTML`:
63 68
64 69 ```elixir
65 70 defmodule MayAppWeb.Admin.ProductController do
66 71 use MyAppWeb, :controller
67 - use Transponder,
68 - responder: Transponder.Responders.HTML
72 + use Transponder, format: Transponder.HTML
69 73
70 74 # defaction ...
71 75 end
  @@ -73,21 +77,21 @@ end
73 77
74 78 And then you'll need to create templates for the actions you use: `index.html.eex`, `show.html.eex`, etc..
75 79
76 - You can also create a custom responder:
80 + You can also create a custom formatter:
77 81
78 82 ```elixir
79 - defmodule MyResponder do
80 - @behaviour Transponder.Responder
83 + defmodule MyFormat do
84 + @behaviour Transponder.Formatter
81 85
82 86 # pattern match and render how you like
83 87 @impl true
84 - def respond(_action, conn, {:ok, bla}) do
88 + def format(_action, conn, {:ok, bla}) do
85 89 conn
86 90 |> put_status(200)
87 91 |> render("yay.html")
88 92 end
89 93
90 - def respond(_action, conn, {:error, bla}) do
94 + def format(_action, conn, {:error, bla}) do
91 95 conn
92 96 |> put_status(401)
93 97 |> render("oops.html")
  @@ -4,7 +4,7 @@
4 4 {<<"elixir">>,<<"~> 1.9">>}.
5 5 {<<"files">>,
6 6 [<<"lib">>,<<"lib/transponder">>,<<"lib/transponder/html.ex">>,
7 - <<"lib/transponder/responder.ex">>,<<"lib/transponder/json.ex">>,
7 + <<"lib/transponder/formatter.ex">>,<<"lib/transponder/json.ex">>,
8 8 <<"lib/transponder.ex">>,<<".formatter.exs">>,<<"mix.exs">>,
9 9 <<"README.md">>]}.
10 10 {<<"licenses">>,[<<"MIT">>]}.
  @@ -31,4 +31,4 @@
31 31 {<<"optional">>,false},
32 32 {<<"repository">>,<<"hexpm">>},
33 33 {<<"requirement">>,<<"~> 1.2">>}]]}.
34 - {<<"version">>,<<"0.1.0">>}.
34 + {<<"version">>,<<"0.1.1">>}.
  @@ -4,12 +4,12 @@ defmodule Transponder do
4 4 """
5 5
6 6 defmacro __using__(opts) do
7 - responder = Keyword.fetch!(opts, :responder)
7 + format = Keyword.fetch!(opts, :format)
8 8
9 9 quote do
10 10 import Transponder
11 11
12 - @responder unquote(responder)
12 + @formatter unquote(format)
13 13 end
14 14 end
15 15
  @@ -17,7 +17,7 @@ defmodule Transponder do
17 17 quote do
18 18 def unquote(name)(conn, params) do
19 19 data = %{conn: conn, params: params, assigns: conn.assigns}
20 - @responder.respond(unquote(name), conn, unquote(fun).(data))
20 + @formatter.format(unquote(name), conn, unquote(fun).(data))
21 21 end
22 22 end
23 23 end
  @@ -0,0 +1,3 @@
1 + defmodule Transponder.Formatter do
2 + @callback format(atom, Plug.Conn.t(), term) :: Plug.Conn.t()
3 + end
  @@ -1,65 +1,67 @@
1 1 defmodule Transponder.HTML do
2 - @behaviour Transponder.Responder
2 + @behaviour Transponder.Formatter
3 3
4 4 import Plug.Conn
5 5 import Phoenix.Controller
6 6
7 7 @impl true
8 - def respond(_action, conn, {:error, :not_found}) do
8 + def format(_action, conn, {:error, :not_found}) do
9 9 conn
10 10 |> put_status(:not_found)
11 11 |> render("404.html")
12 12 end
13 13
14 - def respond(:new, conn, {:ok, record}) do
14 + def format(:new, conn, {:ok, record}) do
15 15 render(conn, "new.html", record: record)
16 16 end
17 17
18 - def respond(:create, conn, {:error, changeset = %Ecto.Changeset{}}) do
18 + def format(:create, conn, {:error, changeset = %Ecto.Changeset{}}) do
19 19 conn
20 20 |> put_status(:unprocessable_entity)
21 21 |> render("new.html", record: changeset.data, changeset: changeset)
22 22 end
23 23
24 - def respond(:create, conn, {:ok, _record}) do
24 + def format(:create, conn, {:ok, _record}) do
25 25 conn
26 26 |> put_flash(:info, "Created")
27 27 |> redirect(to: "/")
28 28 end
29 29
30 - def respond(:edit, conn, {:ok, record}) do
30 + def format(:edit, conn, {:ok, record}) do
31 31 render(conn, "edit.html", record: record)
32 32 end
33 33
34 - def respond(:update, conn, {:error, changeset}) do
34 + def format(:update, conn, {:error, changeset}) do
35 35 conn
36 36 |> put_status(:unprocessable_entity)
37 37 |> render("edit.html", record: changeset.data, changeset: changeset)
38 38 end
39 39
40 - def respond(:update, conn, {:ok, _record}) do
40 + def format(:update, conn, {:ok, _record}) do
41 41 conn
42 42 |> put_flash(:info, "Updated")
43 43 |> redirect(to: "/")
44 44 end
45 45
46 - def respond(:index, conn, {:ok, records}) do
46 + def format(:index, conn, {:ok, records}) do
47 47 render(conn, "index.html", records: records)
48 48 end
49 49
50 - def respond(:show, conn, {:ok, record}) do
50 + def format(:show, conn, {:ok, record}) do
51 51 render(conn, "show.html", record: record)
52 52 end
53 53
54 - def respond(:delete, conn, {:ok, _record}) do
54 + def format(:delete, conn, {:ok, _record}) do
55 55 conn
56 56 |> put_flash(:info, "Deleted")
57 57 |> redirect(to: "/")
58 58 end
59 59
60 - def respond(_action, conn, _response) do
61 - conn
62 - |> put_status(500)
63 - |> render("500.html")
60 + if Mix.env() != :dev do
61 + def format(_action, conn, _response) do
62 + conn
63 + |> put_status(500)
64 + |> render("500.html")
65 + end
64 66 end
65 67 end
Loading more files…