Packages

A REST toolkit for building highly-scalable and fault-tolerant HTTP APIs with Elixir

Current section

6 Versions

Jump to

Compare versions

6 files changed
+97 additions
-162 deletions
  @@ -1,9 +1,8 @@
1 1 {<<"app">>,<<"placid">>}.
2 2 {<<"build_tools">>,[<<"mix">>]}.
3 - {<<"contributors">>,[<<"Shane Logsdon">>]}.
4 3 {<<"description">>,
5 - <<"A REST toolkit for building highly-scalable and fault-tolerant HTTP APIs with Elixir\n">>}.
6 - {<<"elixir">>,<<">= 1.0.0-rc1">>}.
4 + <<"A REST toolkit for building highly-scalable and fault-tolerant HTTP APIs with Elixir">>}.
5 + {<<"elixir">>,<<">= 1.0.0">>}.
7 6 {<<"files">>,
8 7 [<<"lib/mix/placid/serve.ex">>,<<"lib/placid.ex">>,
9 8 <<"lib/placid/config.ex">>,<<"lib/placid/handler.ex">>,
  @@ -15,22 +14,34 @@
15 14 <<"lib/placid/response/rendering/engines/json.ex">>,
16 15 <<"lib/placid/response/rendering/engines/xml.ex">>,
17 16 <<"lib/placid/response/status_code.ex">>,<<"lib/placid/router.ex">>,
18 - <<"lib/placid/router/util.ex">>,<<"mix.exs">>,<<"README.md">>,
19 - <<"LICENSE">>]}.
17 + <<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}.
20 18 {<<"licenses">>,[<<"MIT">>]}.
21 - {<<"links">>,#{<<"GitHub">> => <<"https://github.com/slogsdon/placid">>}}.
19 + {<<"links">>,[{<<"GitHub">>,<<"https://github.com/slogsdon/placid">>}]}.
20 + {<<"maintainers">>,[<<"Shane Logsdon">>]}.
22 21 {<<"name">>,<<"placid">>}.
23 22 {<<"requirements">>,
24 - #{<<"cowboy">> => #{<<"app">> => <<"cowboy">>,<<"optional">> => nil,<<"requirement">> => <<"~> 1.0.0">>},
25 - <<"http_router">> => #{<<"app">> => <<"http_router">>,
26 - <<"optional">> => nil,
27 - <<"requirement">> => <<"~> 0.0.3">>},
28 - <<"linguist">> => #{<<"app">> => <<"linguist">>,
29 - <<"optional">> => nil,
30 - <<"requirement">> => <<"~> 0.1.2">>},
31 - <<"plug">> => #{<<"app">> => <<"plug">>,<<"optional">> => nil,<<"requirement">> => <<"~> 0.9.0">>},
32 - <<"poison">> => #{<<"app">> => <<"poison">>,<<"optional">> => nil,<<"requirement">> => <<"~> 1.2.0">>},
33 - <<"xml_builder">> => #{<<"app">> => <<"xml_builder">>,
34 - <<"optional">> => nil,
35 - <<"requirement">> => <<"~> 0.0.5">>}}}.
36 - {<<"version">>,<<"0.1.3">>}.
23 + [[{<<"app">>,<<"cowboy">>},
24 + {<<"name">>,<<"cowboy">>},
25 + {<<"optional">>,false},
26 + {<<"requirement">>,<<"~> 1.0">>}],
27 + [{<<"app">>,<<"plug">>},
28 + {<<"name">>,<<"plug">>},
29 + {<<"optional">>,false},
30 + {<<"requirement">>,<<"~> 1.0">>}],
31 + [{<<"app">>,<<"http_router">>},
32 + {<<"name">>,<<"http_router">>},
33 + {<<"optional">>,false},
34 + {<<"requirement">>,<<"~> 0.0">>}],
35 + [{<<"app">>,<<"linguist">>},
36 + {<<"name">>,<<"linguist">>},
37 + {<<"optional">>,false},
38 + {<<"requirement">>,<<"~> 0.1">>}],
39 + [{<<"app">>,<<"poison">>},
40 + {<<"name">>,<<"poison">>},
41 + {<<"optional">>,false},
42 + {<<"requirement">>,<<"~> 3.0">>}],
43 + [{<<"app">>,<<"xml_builder">>},
44 + {<<"name">>,<<"xml_builder">>},
45 + {<<"optional">>,false},
46 + {<<"requirement">>,<<"~> 0.0">>}]]}.
47 + {<<"version">>,<<"0.2.0">>}.
  @@ -2,12 +2,12 @@ defmodule Placid.Handler do
2 2 @moduledoc """
3 3 Handlers facilitate some separation of concerns for your application's logic.
4 4
5 - All handler actions should have an arrity of 2, with the first argument being
6 - a `Plug.Conn` representing the current connection and the second argument
5 + All handler actions should have an arrity of 2, with the first argument being
6 + a `Plug.Conn` representing the current connection and the second argument
7 7 being a `Keyword` list of any parameters captured in the route path.
8 8
9 9 `Placid.Handler` imports `Plug.Conn`, the `plug/1` and `plug/2` macros from
10 - `Plug.Builder`, `Placid.Handler`, and `Placid.Response.Helpers` for
10 + `Plug.Builder`, `Placid.Handler`, and `Placid.Response.Helpers` for
11 11 convenience when creating handlers for your applications
12 12
13 13 ## Example
  @@ -25,7 +25,7 @@ defmodule Placid.Handler do
25 25 @doc false
26 26 def show(conn, args) do
27 27 result = case Integer.parse args["page_id"] do
28 - :error ->
28 + :error ->
29 29 %Error{ id: "no_page_id",
30 30 message: "A valid page_id is required." }
31 31 {i, _} ->
  @@ -43,7 +43,7 @@ defmodule Placid.Handler do
43 43 @doc false
44 44 def update(conn, args) do
45 45 result = case Integer.parse args["page_id"] do
46 - :error ->
46 + :error ->
47 47 %Error{ id: "no_page_id",
48 48 message: "A valid page_id is requried." }
49 49 {i, _} ->
  @@ -75,7 +75,7 @@ defmodule Placid.Handler do
75 75 { plug, Keyword.put_new(opts, :run, :before), guard }
76 76 end)
77 77
78 - plug_stacks = build_plug_stacks plugs
78 + plug_stacks = build_plug_stacks env, plugs
79 79
80 80 quote do
81 81 def init(opts) do
  @@ -94,17 +94,17 @@ defmodule Placid.Handler do
94 94 end
95 95 end
96 96
97 - defp build_plug_stacks(plugs) do
97 + defp build_plug_stacks(env, plugs) do
98 98 only_actions = get_only_actions plugs
99 99
100 100 Enum.map only_actions ++ [nil], fn action ->
101 - build_plug_stacks_for action, plugs
101 + build_plug_stacks_for action, env, plugs
102 102 end
103 103 end
104 104
105 - defp build_plug_stacks_for(action, plugs) do
106 - before_body = build_calls_for(:before, action, plugs)
107 - after_body = build_calls_for(:after, action, plugs)
105 + defp build_plug_stacks_for(action, env, plugs) do
106 + before_body = build_calls_for(:before, action, env, plugs)
107 + after_body = build_calls_for(:after, action, env, plugs)
108 108
109 109 quote do
110 110 unquote(before_body)
  @@ -112,15 +112,15 @@ defmodule Placid.Handler do
112 112 end
113 113 end
114 114
115 - defp build_calls_for(before_or_after, nil, plugs) do
116 - { conn, body } = plugs
117 - |> Enum.filter(fn { _, opts, _ } ->
118 - opts[:only] === nil
119 - end)
120 - |> Enum.filter(fn { _, opts, _ } ->
121 - opts[:run] === before_or_after
122 - end)
123 - |> Plug.Builder.compile
115 + defp build_calls_for(before_or_after, nil, env, plugs) do
116 + plugs = plugs
117 + |> Enum.filter(fn { _, opts, _ } ->
118 + opts[:only] === nil
119 + end)
120 + |> Enum.filter(fn { _, opts, _ } ->
121 + opts[:run] === before_or_after
122 + end)
123 + { conn, body } = env |> Plug.Builder.compile(plugs, [])
124 124
125 125 quote do
126 126 defp do_call(unquote(conn), unquote(before_or_after), _) do
  @@ -128,17 +128,17 @@ defmodule Placid.Handler do
128 128 end
129 129 end
130 130 end
131 - defp build_calls_for(before_or_after, action, plugs) do
132 - { conn, body } = plugs
133 - |> Enum.filter(fn { _, opts, _ } ->
134 - opts[:only] === nil ||
135 - action === opts[:only] ||
136 - action in opts[:only]
137 - end)
138 - |> Enum.filter(fn { _, opts, _ } ->
139 - opts[:run] === before_or_after
140 - end)
141 - |> Plug.Builder.compile
131 + defp build_calls_for(before_or_after, action, env, plugs) do
132 + plugs = plugs
133 + |> Enum.filter(fn { _, opts, _ } ->
134 + opts[:only] === nil ||
135 + action === opts[:only] ||
136 + action in opts[:only]
137 + end)
138 + |> Enum.filter(fn { _, opts, _ } ->
139 + opts[:run] === before_or_after
140 + end)
141 + { conn, body } = env |> Plug.Builder.compile(plugs, [])
142 142
143 143 quote do
144 144 defp do_call(unquote(conn), unquote(before_or_after), unquote(action)) do
  @@ -157,4 +157,4 @@ defmodule Placid.Handler do
157 157 end)
158 158 |> Enum.uniq
159 159 end
160 - end
\ No newline at end of file
160 + end
  @@ -1,6 +1,6 @@
1 1 defmodule Placid.Response.Helpers do
2 2 @moduledoc """
3 - Placid bundles these response helpers with handlers to
3 + Placid bundles these response helpers with handlers to
4 4 assist in sending a response.
5 5
6 6 #### Example
  @@ -47,7 +47,7 @@ defmodule Placid.Response.Helpers do
47 47 def status(conn, status_code) when is_integer(status_code)
48 48 when is_atom(status_code) do
49 49 %StatusCode{ code: code } = status_code |> StatusCode.find
50 - %Plug.Conn{ conn | status: code,
50 + %Plug.Conn{ conn | status: code,
51 51 state: :set }
52 52 end
53 53
  @@ -65,7 +65,7 @@ defmodule Placid.Response.Helpers do
65 65 """
66 66 @spec headers(Plug.Conn.t, Plug.Conn.headers) :: Plug.Conn.t
67 67 def headers(conn, headers) do
68 - %Plug.Conn{ conn | resp_headers: headers,
68 + %Plug.Conn{ conn | resp_headers: headers,
69 69 state: :set }
70 70 end
71 71
  @@ -130,7 +130,7 @@ defmodule Placid.Response.Helpers do
130 130 """
131 131 @spec halt!(Plug.Conn.t, Keyword.t) :: Plug.Conn.t
132 132 def halt!(conn, opts \\ []) do
133 - opts = [ status: 401] |> Keyword.merge opts
133 + opts = [ status: 401] |> Keyword.merge(opts)
134 134 %StatusCode{ code: code, reason: reason } = opts[:status] |> StatusCode.find
135 135 conn
136 136 |> send_resp_if_not_sent(code, reason)
  @@ -187,10 +187,10 @@ defmodule Placid.Response.Helpers do
187 187 """
188 188 @spec redirect(Plug.Conn.t, binary, Keyword.t) :: Plug.Conn.t
189 189 def redirect(conn, location, opts \\ []) do
190 - opts = [ status: 302 ] |> Keyword.merge opts
191 - %StatusCode{code: code, reason: reason} = opts[:status] |> StatusCode.find
190 + opts = [ status: 302 ] |> Keyword.merge(opts)
191 + %StatusCode{code: code, reason: reason} = opts[:status] |> StatusCode.find
192 192 conn
193 - |> put_resp_header_if_not_sent("Location", location)
193 + |> put_resp_header_if_not_sent("location", location)
194 194 |> send_resp_if_not_sent(code, reason)
195 195 end
  @@ -4,6 +4,19 @@ defmodule Placid.Router do
4 4 quote do
5 5 use HttpRouter,
6 6 default_content_type: "application/json; charset=utf-8"
7 +
8 + def run(opts \\ nil) do
9 + adapter = Placid.Config.get(:placid, :plug_adapter, Plug.Adapters.Cowboy)
10 + opts = opts || Placid.Config.get(__MODULE__)
11 +
12 + adapter.https __MODULE__, [], opts[:https]
13 + if opts[:https_only] do
14 + # Sends `403 Forbidden` to all HTTP requests
15 + adapter.http Placid.Request.HttpsOnly, [], opts[:http]
16 + else
17 + adapter.http __MODULE__, [], opts[:http]
18 + end
19 + end
7 20 end
8 21 end
9 22 end
  @@ -1,90 +0,0 @@
1 - defmodule Placid.Router.Util do
2 -
3 - defmodule InvalidSpecError do
4 - defexception message: "invalid route specification"
5 - end
6 -
7 - def normalize_method(method) do
8 - method |> to_string |> String.upcase
9 - end
10 -
11 - def split(bin) do
12 - for segment <- String.split(bin, "/"), segment != "", do: segment
13 - end
14 -
15 - def build_spec(spec, context \\ nil)
16 - def build_spec(spec, context) when is_binary(spec) do
17 - build_spec split(spec), context, [], []
18 - end
19 - def build_spec(spec, _context) do
20 - {[], spec}
21 - end
22 - defp build_spec([h|t], context, vars, acc) do
23 - handle_segment_match segment_match(h, "", context), t, context, vars, acc
24 - end
25 - defp build_spec([], _context, vars, acc) do
26 - {vars |> Enum.uniq |> Enum.reverse, Enum.reverse(acc)}
27 - end
28 -
29 - defp handle_segment_match({:literal, literal}, t, context, vars, acc) do
30 - build_spec t, context, vars, [literal|acc]
31 - end
32 - defp handle_segment_match({:identifier, identifier, expr}, t, context, vars, acc) do
33 - build_spec t, context, [identifier|vars], [expr|acc]
34 - end
35 - defp handle_segment_match({:glob, identifier, expr}, t, context, vars, acc) do
36 - if t != [] do
37 - raise InvalidSpecError, message: "cannot have a *glob followed by other segments"
38 - end
39 -
40 - case acc do
41 - [hs|ts] ->
42 - acc = [{:|, [], [hs, expr]} | ts]
43 - build_spec([], context, [identifier|vars], acc)
44 - _ ->
45 - {vars, expr} = build_spec([], context, [identifier|vars], [expr])
46 - {vars, hd(expr)}
47 - end
48 - end
49 -
50 - defp segment_match(":" <> argument, buffer, context) do
51 - identifier = binary_to_identifier(":", argument)
52 - expr = quote_if_buffer identifier, buffer, context, fn var ->
53 - quote do: unquote(buffer) <> unquote(var)
54 - end
55 - {:identifier, identifier, expr}
56 - end
57 - defp segment_match("*" <> argument, buffer, context) do
58 - underscore = {:_, [], context}
59 - identifier = binary_to_identifier("*", argument)
60 - expr = quote_if_buffer identifier, buffer, context, fn var ->
61 - quote do: [unquote(buffer) <> unquote(underscore)|unquote(underscore)] = unquote(var)
62 - end
63 - {:glob, identifier, expr}
64 - end
65 - defp segment_match(<<h, t::binary>>, buffer, context) do
66 - segment_match t, buffer <> <<h>>, context
67 - end
68 - defp segment_match(<<>>, buffer, _context) do
69 - {:literal, buffer}
70 - end
71 -
72 - defp quote_if_buffer(identifier, "", context, _fun) do
73 - {identifier, [], context}
74 - end
75 - defp quote_if_buffer(identifier, _buffer, context, fun) do
76 - fun.({identifier, [], context})
77 - end
78 -
79 - defp binary_to_identifier(prefix, <<letter, _::binary>> = binary) when letter in ?a..?z
80 - or letter == ?_ do
81 - if binary =~ ~r/^\w+$/ do
82 - String.to_atom(binary)
83 - else
84 - raise InvalidSpecError, message: "#{prefix}identifier in routes must be made of letters, numbers and underscore"
85 - end
86 - end
87 - defp binary_to_identifier(prefix, _) do
88 - raise InvalidSpecError, message: "#{prefix} in routes must be followed by lowercase letters"
89 - end
90 - end
\ No newline at end of file
Loading more files…