|
1 | | -defmodule Color.Palette.Visualizer.Standalone do |
2 | | - @moduledoc """ |
3 | | - A tiny helper that runs `Color.Palette.Visualizer` as a |
4 | | - standalone web server for local development. |
5 | | -
|
6 | | - Requires `:bandit` in your project's deps. |
7 | | -
|
8 | | - Color.Palette.Visualizer.Standalone.start(port: 4001) |
9 | | - # Visit http://localhost:4001 |
10 | | -
|
11 | | - To stop the server, call |
12 | | - `Color.Palette.Visualizer.Standalone.stop/1` with the PID |
13 | | - returned from `start/1`. |
14 | | -
|
15 | | - """ |
16 | | - |
17 | | - @doc """ |
18 | | - Starts the visualizer on the given port. |
19 | | -
|
20 | | - ### Options |
21 | | -
|
22 | | - * `:port` — TCP port to listen on. Default `4001`. |
23 | | -
|
24 | | - * `:ip` — IP address to bind to. Default `:loopback` (only |
25 | | - accessible from localhost). Pass `:any` to bind on all |
26 | | - interfaces. |
27 | | -
|
28 | | - ### Returns |
29 | | -
|
30 | | - * `{:ok, pid}` on success. |
31 | | -
|
32 | | - * `{:error, reason}` on failure — most commonly a port-in-use |
33 | | - error. |
34 | | -
|
35 | | - """ |
36 | | - @spec start(keyword()) :: {:ok, pid()} | {:error, term()} |
37 | | - def start(options \\ []) do |
38 | | - ensure_bandit!() |
39 | | - |
40 | | - port = Keyword.get(options, :port, 4001) |
41 | | - ip = Keyword.get(options, :ip, :loopback) |
42 | | - |
43 | | - bandit_options = [ |
44 | | - plug: Color.Palette.Visualizer, |
45 | | - port: port, |
46 | | - ip: ip_tuple(ip) |
47 | | - ] |
48 | | - |
49 | | - Bandit.start_link(bandit_options) |
50 | | - end |
51 | | - |
52 | | - @doc """ |
53 | | - Returns a child spec suitable for embedding under a |
54 | | - supervision tree. |
| 1 | +# `Color.Palette.Visualizer.Standalone` runs the visualizer as |
| 2 | +# a self-contained Bandit web server. It is only defined when |
| 3 | +# `:bandit` is available (which transitively pulls in `:plug`), |
| 4 | +# so consumer apps that don't need a standalone dev server |
| 5 | +# don't get compiler warnings about missing modules. When |
| 6 | +# `:bandit` isn't installed, this module simply doesn't exist |
| 7 | +# and any call to it raises the standard |
| 8 | +# `UndefinedFunctionError`. |
| 9 | +if Code.ensure_loaded?(Bandit) do |
| 10 | + defmodule Color.Palette.Visualizer.Standalone do |
| 11 | + @moduledoc """ |
| 12 | + A tiny helper that runs `Color.Palette.Visualizer` as a |
| 13 | + standalone web server for local development. |
| 14 | +
|
| 15 | + Requires `:bandit` in your project's deps. This module is |
| 16 | + only compiled when `:bandit` is present — without it, |
| 17 | + `Color.Palette.Visualizer.Standalone` doesn't exist as a |
| 18 | + module and calling its functions raises |
| 19 | + `UndefinedFunctionError`. |
| 20 | +
|
| 21 | + Color.Palette.Visualizer.Standalone.start(port: 4001) |
| 22 | + # Visit http://localhost:4001 |
| 23 | +
|
| 24 | + To stop the server, call |
| 25 | + `Color.Palette.Visualizer.Standalone.stop/1` with the PID |
| 26 | + returned from `start/1`. |
| 27 | +
|
| 28 | + """ |
| 29 | + |
| 30 | + @doc """ |
| 31 | + Starts the visualizer on the given port. |
| 32 | +
|
| 33 | + ### Options |
| 34 | +
|
| 35 | + * `:port` — TCP port to listen on. Default `4001`. |
| 36 | +
|
| 37 | + * `:ip` — IP address to bind to. Default `:loopback` (only |
| 38 | + accessible from localhost). Pass `:any` to bind on all |
| 39 | + interfaces. |
| 40 | +
|
| 41 | + ### Returns |
| 42 | +
|
| 43 | + * `{:ok, pid}` on success. |
| 44 | +
|
| 45 | + * `{:error, reason}` on failure — most commonly a port-in-use |
| 46 | + error. |
| 47 | +
|
| 48 | + """ |
| 49 | + @spec start(keyword()) :: {:ok, pid()} | {:error, term()} |
| 50 | + def start(options \\ []) do |
| 51 | + port = Keyword.get(options, :port, 4001) |
| 52 | + ip = Keyword.get(options, :ip, :loopback) |
| 53 | + |
| 54 | + bandit_options = [ |
| 55 | + plug: Color.Palette.Visualizer, |
| 56 | + port: port, |
| 57 | + ip: ip_tuple(ip) |
| 58 | + ] |
| 59 | + |
| 60 | + Bandit.start_link(bandit_options) |
| 61 | + end |
55 | 62 |
|
56 | | - ### Options |
| 63 | + @doc """ |
| 64 | + Returns a child spec suitable for embedding under a |
| 65 | + supervision tree. |
57 | 66 |
|
58 | | - See `start/1`. |
| 67 | + ### Options |
59 | 68 |
|
60 | | - ### Returns |
| 69 | + See `start/1`. |
61 | 70 |
|
62 | | - * A child specification map. |
| 71 | + ### Returns |
63 | 72 |
|
64 | | - """ |
65 | | - @spec child_spec(keyword()) :: Supervisor.child_spec() |
66 | | - def child_spec(options \\ []) do |
67 | | - ensure_bandit!() |
| 73 | + * A child specification map. |
68 | 74 |
|
69 | | - port = Keyword.get(options, :port, 4001) |
70 | | - ip = Keyword.get(options, :ip, :loopback) |
| 75 | + """ |
| 76 | + @spec child_spec(keyword()) :: Supervisor.child_spec() |
| 77 | + def child_spec(options \\ []) do |
| 78 | + port = Keyword.get(options, :port, 4001) |
| 79 | + ip = Keyword.get(options, :ip, :loopback) |
71 | 80 |
|
72 | | - %{ |
73 | | - id: __MODULE__, |
74 | | - start: |
75 | | - {Bandit, :start_link, [[plug: Color.Palette.Visualizer, port: port, ip: ip_tuple(ip)]]}, |
76 | | - type: :supervisor |
77 | | - } |
78 | | - end |
| 81 | + %{ |
| 82 | + id: __MODULE__, |
| 83 | + start: |
| 84 | + {Bandit, :start_link, [[plug: Color.Palette.Visualizer, port: port, ip: ip_tuple(ip)]]}, |
| 85 | + type: :supervisor |
| 86 | + } |
| 87 | + end |
79 | 88 |
|
80 | | - @doc """ |
81 | | - Stops a standalone server started by `start/1`. |
| 89 | + @doc """ |
| 90 | + Stops a standalone server started by `start/1`. |
82 | 91 |
|
83 | | - ### Arguments |
| 92 | + ### Arguments |
84 | 93 |
|
85 | | - * `pid` — the process identifier returned by `start/1`. |
| 94 | + * `pid` — the process identifier returned by `start/1`. |
86 | 95 |
|
87 | | - ### Returns |
| 96 | + ### Returns |
88 | 97 |
|
89 | | - * `:ok`. |
| 98 | + * `:ok`. |
90 | 99 |
|
91 | | - """ |
92 | | - @spec stop(pid()) :: :ok |
93 | | - def stop(pid) when is_pid(pid) do |
94 | | - _ = Supervisor.stop(pid) |
95 | | - :ok |
96 | | - end |
| 100 | + """ |
| 101 | + @spec stop(pid()) :: :ok |
| 102 | + def stop(pid) when is_pid(pid) do |
| 103 | + _ = Supervisor.stop(pid) |
| 104 | + :ok |
| 105 | + end |
97 | 106 |
|
98 | | - # ---- helpers ------------------------------------------------------------ |
| 107 | + # ---- helpers ------------------------------------------------------------ |
99 | 108 |
|
100 | | - defp ensure_bandit! do |
101 | | - unless Code.ensure_loaded?(Bandit) do |
102 | | - raise """ |
103 | | - Color.Palette.Visualizer.Standalone requires :bandit. |
104 | | - Add `{:bandit, "~> 1.5"}` to your project's deps and run mix deps.get. |
105 | | - """ |
106 | | - end |
| 109 | + defp ip_tuple(:loopback), do: {127, 0, 0, 1} |
| 110 | + defp ip_tuple(:any), do: {0, 0, 0, 0} |
| 111 | + defp ip_tuple({_, _, _, _} = tuple), do: tuple |
107 | 112 | end |
108 | | - |
109 | | - defp ip_tuple(:loopback), do: {127, 0, 0, 1} |
110 | | - defp ip_tuple(:any), do: {0, 0, 0, 0} |
111 | | - defp ip_tuple({_, _, _, _} = tuple), do: tuple |
112 | 113 | end |
0 commit comments