4 | <%= @inner_content %>
5 |
6 |
--------------------------------------------------------------------------------
/lib/demo.ex:
--------------------------------------------------------------------------------
1 | defmodule Demo do
2 | @moduledoc """
3 | Demo keeps the contexts that define your domain
4 | and business logic.
5 |
6 | Contexts are also responsible for managing your data, regardless
7 | if it comes from the database, an external API or others.
8 | """
9 | end
10 |
--------------------------------------------------------------------------------
/lib/demo_web/views/layout_view.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.LayoutView do
2 | use DemoWeb, :view
3 |
4 | # Phoenix LiveDashboard is available only in development by default,
5 | # so we instruct Elixir to not warn if the dashboard route is missing.
6 | @compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
7 | end
8 |
--------------------------------------------------------------------------------
/test/demo_web/views/layout_view_test.exs:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.LayoutViewTest do
2 | use DemoWeb.ConnCase, async: true
3 |
4 | # When testing helpers, you may want to import Phoenix.HTML and
5 | # use functions such as safe_to_string() to convert the helper
6 | # result into an HTML string.
7 | # import Phoenix.HTML
8 | end
9 |
--------------------------------------------------------------------------------
/lib/demo_web/channels/presence.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.Presence do
2 | @moduledoc """
3 | Provides presence tracking to channels and processes.
4 |
5 | See the [`Phoenix.Presence`](http://hexdocs.pm/phoenix/Phoenix.Presence.html)
6 | docs for more details.
7 | """
8 | use Phoenix.Presence, otp_app: :demo,
9 | pubsub_server: Demo.PubSub
10 | end
11 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20180610040824_create_users.exs:
--------------------------------------------------------------------------------
1 | defmodule Demo.Repo.Migrations.CreateUsers do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:users) do
6 | add(:username, :string)
7 | add(:email, :string)
8 | add(:phone_number, :string)
9 |
10 | timestamps()
11 | end
12 |
13 | create(unique_index(:users, :email))
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/demo_web/templates/layout/live.html.heex:
--------------------------------------------------------------------------------
1 |
2 |
<%= live_flash(@flash, :info) %>
5 |
6 |
<%= live_flash(@flash, :error) %>
9 |
10 | <%= @inner_content %>
11 |
12 |
--------------------------------------------------------------------------------
/test/demo_web/views/error_view_test.exs:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.ErrorViewTest do
2 | use DemoWeb.ConnCase, async: true
3 |
4 | # Bring render/3 and render_to_string/3 for testing custom views
5 | import Phoenix.View
6 |
7 | test "renders 404.html" do
8 | assert render_to_string(DemoWeb.ErrorView, "404.html", []) == "Not Found"
9 | end
10 |
11 | test "renders 500.html" do
12 | assert render_to_string(DemoWeb.ErrorView, "500.html", []) == "Internal Server Error"
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/lib/demo_web/views/error_view.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.ErrorView do
2 | use DemoWeb, :view
3 |
4 | # If you want to customize a particular status code
5 | # for a certain format, you may uncomment below.
6 | # def render("500.html", _assigns) do
7 | # "Internal Server Error"
8 | # end
9 |
10 | # By default, Phoenix returns the status message from
11 | # the template name. For example, "404.html" becomes
12 | # "Not Found".
13 | def template_not_found(template, _assigns) do
14 | Phoenix.Controller.status_message_from_template(template)
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/priv/repo/seeds.exs:
--------------------------------------------------------------------------------
1 | # Script for populating the database. You can run it as:
2 | #
3 | # mix run priv/repo/seeds.exs
4 | #
5 | # Inside the script, you can read and write to any of your
6 | # repositories directly:
7 | #
8 | # Demo.Repo.insert!(%Demo.SomeSchema{})
9 | #
10 | # We recommend using the bang functions (`insert!`, `update!`
11 | # and so on) as they will fail if something goes wrong.
12 |
13 | for i <- 1..1000 do
14 | {:ok, _} =
15 | Demo.Accounts.create_user(%{
16 | username: "user#{i}",
17 | name: "User #{i}",
18 | email: "user#{i}@test",
19 | phone_number: "555-555-5555"
20 | })
21 | end
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | To start your Phoenix server:
4 |
5 | * Install dependencies with `mix deps.get`
6 | * Create and migrate your database with `mix ecto.setup`
7 | * Start Phoenix endpoint with `mix phx.server` or inside IEx with `iex -S mix phx.server`
8 |
9 | Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
10 |
11 | Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
12 |
13 | ## Learn more
14 |
15 | * Official website: https://www.phoenixframework.org/
16 | * Guides: https://hexdocs.pm/phoenix/overview.html
17 | * Docs: https://hexdocs.pm/phoenix
18 | * Forum: https://elixirforum.com/c/phoenix-forum
19 | * Source: https://github.com/phoenixframework/phoenix
20 |
--------------------------------------------------------------------------------
/lib/demo_web/gettext.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.Gettext do
2 | @moduledoc """
3 | A module providing Internationalization with a gettext-based API.
4 |
5 | By using [Gettext](https://hexdocs.pm/gettext),
6 | your module gains a set of macros for translations, for example:
7 |
8 | import DemoWeb.Gettext
9 |
10 | # Simple translation
11 | gettext("Here is the string to translate")
12 |
13 | # Plural translation
14 | ngettext("Here is the string to translate",
15 | "Here are the strings to translate",
16 | 3)
17 |
18 | # Domain-based translation
19 | dgettext("errors", "Here is the error message to translate")
20 |
21 | See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
22 | """
23 | use Gettext, otp_app: :demo
24 | end
25 |
--------------------------------------------------------------------------------
/lib/demo_web/live/top_live.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.TopLive do
2 | use DemoWeb, :live_view
3 |
4 | def render(assigns) do
5 | ~H"""
6 |
7 | <%= @top %>
8 |
9 | """
10 | end
11 |
12 | def mount(_params, _session, socket) do
13 | if connected?(socket), do: :timer.send_interval(1000, self(), :tick)
14 |
15 | {:ok, put_top(socket)}
16 | end
17 |
18 | def handle_info(:tick, socket) do
19 | {:noreply, put_top(socket)}
20 | end
21 |
22 | defp put_top(socket) do
23 | case :os.type() do
24 | {:unix, :darwin} ->
25 | {top, 0} = System.cmd("top", ["-l", "1"])
26 | assign(socket, top: top)
27 | {:unix, :linux} ->
28 | {top, 0} = System.cmd("top", ["-n", "1", "-b"])
29 | assign(socket, top: top)
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/demo_web/live/clock_live.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.ClockLive do
2 | use DemoWeb, :live_view
3 |
4 | def render(assigns) do
5 | ~H"""
6 |
21 | <%= live_redirect "Show", to: Routes.user_show_path(@socket, :show, user) %>
22 | <%= live_redirect "Edit", to: Routes.user_edit_path(@socket, :edit, user) %>
23 | <%= link "Delete", to: "#",
24 | phx_click: "delete_user",
25 | phx_value_id: user.id %>
26 |
27 |
28 | <% end %>
29 |
30 |
31 |
32 | <%= if @page > 1 do %>
33 | <%= live_patch "prev", to: Routes.user_index_path(@socket, :index, page: @page - 1) %>
34 | <% end %>
35 | <%= for i <- (@page - 5)..(@page + 5), i > 0 do %>
36 | <%= live_patch "#{i}", to: Routes.user_index_path(@socket, :index, page: i) %>
37 | <% end %>
38 | <%= live_patch "next", to: Routes.user_index_path(@socket, :index, page: @page + 1) %>
39 |
40 | <%= live_redirect "New User (live)", to: Routes.user_new_path(@socket, :new) %>
41 |
--------------------------------------------------------------------------------
/lib/demo_web/templates/layout/root.html.heex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%= csrf_meta_tag() %>
8 | <%= live_title_tag assigns[:page_title] || "Demo", suffix: " · Phoenix Framework" %>
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
25 |
26 |
27 |
28 | <%= @inner_content %>
29 |
30 |
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where 3rd-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | turbo-*.tar
24 |
25 | # If NPM crashes, it generates a log, let's ignore it too.
26 | npm-debug.log
27 |
28 | # The directory NPM downloads your dependencies sources to.
29 | /assets/node_modules/
30 |
31 | # Since we are building assets from assets/,
32 | # we ignore priv/static. You may want to comment
33 | # this depending on your deployment strategy.
34 | /priv/static/
35 |
36 | # Files matching config/*.secret.exs pattern contain sensitive
37 | # data and you should not commit them into version control.
38 | #
39 | # Alternatively, you may comment the line below and commit the
40 | # secrets files as long as you replace their contents by environment
41 | # variables.
42 | /config/*.secret.exs
43 | .elixir_ls
--------------------------------------------------------------------------------
/lib/demo_web/live/user_live/index.html.heex:
--------------------------------------------------------------------------------
1 |
Listing Users, page <%= @page %>
2 |
3 |
4 |
5 |
6 |
Username
7 |
Email
8 |
Phone #
9 |
10 |
11 |
12 |
13 |
14 | <%= for user <- @users do %>
15 |
16 |
<%= user.username %>
17 |
<%= user.email %>
18 |
<%= user.phone_number %>
19 |
20 |
21 | <%= live_redirect "Show", to: Routes.user_show_path(@socket, :show, user) %>
22 | <%= live_redirect "Edit", to: Routes.user_edit_path(@socket, :edit, user) %>
23 | <%= link "Delete", to: "#",
24 | phx_click: "delete_user",
25 | phx_value_id: user.id %>
26 |
27 |
28 | <% end %>
29 |
30 |
31 |
32 | <%= if @page > 1 do %>
33 | <%= live_patch "prev", to: Routes.user_index_path(@socket, :index, page: @page - 1) %>
34 | <% end %>
35 | <%= for i <- (@page - 5)..(@page + 5), i > 0 do %>
36 | <%= live_patch "#{i}", to: Routes.user_index_path(@socket, :index, page: i) %>
37 | <% end %>
38 | <%= live_patch "next", to: Routes.user_index_path(@socket, :index, page: @page + 1) %>
39 |
40 | <%= live_redirect "New User (live)", to: Routes.user_new_path(@socket, :new) %>
41 |
--------------------------------------------------------------------------------
/test/support/conn_case.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.ConnCase do
2 | @moduledoc """
3 | This module defines the test case to be used by
4 | tests that require setting up a connection.
5 |
6 | Such tests rely on `Phoenix.ConnTest` and also
7 | import other functionality to make it easier
8 | to build common data structures and query the data layer.
9 |
10 | Finally, if the test case interacts with the database,
11 | we enable the SQL sandbox, so changes done to the database
12 | are reverted at the end of every test. If you are using
13 | PostgreSQL, you can even run database tests asynchronously
14 | by setting `use DemoWeb.ConnCase, async: true`, although
15 | this option is not recommended for other databases.
16 | """
17 |
18 | use ExUnit.CaseTemplate
19 |
20 | using do
21 | quote do
22 | # Import conveniences for testing with connections
23 | import Plug.Conn
24 | import Phoenix.ConnTest
25 | import DemoWeb.ConnCase
26 |
27 | alias DemoWeb.Router.Helpers, as: Routes
28 |
29 | # The default endpoint for testing
30 | @endpoint DemoWeb.Endpoint
31 | end
32 | end
33 |
34 | setup tags do
35 | pid = Ecto.Adapters.SQL.Sandbox.start_owner!(Demo.Repo, shared: not tags[:async])
36 | on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
37 | {:ok, conn: Phoenix.ConnTest.build_conn()}
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/lib/demo_web/live/user_live/show.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.UserLive.Show do
2 | use DemoWeb, :live_view
3 |
4 | alias Demo.Accounts
5 | alias Phoenix.LiveView.Socket
6 |
7 | def render(assigns) do
8 | ~H"""
9 |
Show User
10 |
11 |
Username: <%= @user.username %>
12 |
Email: <%= @user.email %>
13 |
Phone: <%= @user.phone_number %>
14 |
15 | <%= live_redirect "Edit", to: Routes.user_edit_path(@socket, :edit, @user) %>
16 | <%= live_redirect "Back", to: Routes.user_index_path(@socket, :index) %>
17 | """
18 | end
19 |
20 | def mount(_params, _session, socket) do
21 | {:ok, socket}
22 | end
23 |
24 | def handle_params(%{"id" => id}, _url, socket) do
25 | if connected?(socket), do: Demo.Accounts.subscribe(id)
26 | {:noreply, socket |> assign(id: id) |> fetch()}
27 | end
28 |
29 | defp fetch(%Socket{assigns: %{id: id}} = socket) do
30 | assign(socket, user: Accounts.get_user!(id))
31 | end
32 |
33 | def handle_info({Accounts, [:user, :updated], _}, socket) do
34 | {:noreply, fetch(socket)}
35 | end
36 |
37 | def handle_info({Accounts, [:user, :deleted], _}, socket) do
38 | {:noreply,
39 | socket
40 | |> put_flash(:error, "This user has been deleted from the system")
41 | |> push_redirect(to: Routes.user_index_path(socket, :index))}
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/demo_web/live/search_live.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.SearchLive do
2 | use DemoWeb, :live_view
3 |
4 | def render(assigns) do
5 | ~H"""
6 |
15 | """
16 | end
17 |
18 | def mount(_params, _session, socket) do
19 | {:ok, assign(socket, query: nil, result: nil, loading: false, matches: [])}
20 | end
21 |
22 | def handle_event("suggest", %{"q" => query}, socket) when byte_size(query) <= 100 do
23 | {words, _} = System.cmd("grep", ~w"^#{query}.* -m 5 /usr/share/dict/words")
24 | {:noreply, assign(socket, matches: String.split(words, "\n"))}
25 | end
26 |
27 | def handle_event("search", %{"q" => query}, socket) when byte_size(query) <= 100 do
28 | send(self(), {:search, query})
29 | {:noreply, assign(socket, query: query, result: "Searching...", loading: true, matches: [])}
30 | end
31 |
32 | def handle_info({:search, query}, socket) do
33 | {result, _} = System.cmd("dict", ["#{query}"], stderr_to_stdout: true)
34 | {:noreply, assign(socket, loading: false, result: result, matches: [])}
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/demo_web/live/thermostat_live.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.ThermostatLive do
2 | use DemoWeb, :live_view
3 |
4 | def render(assigns) do
5 | ~H"""
6 |
20 | """
21 | end
22 |
23 | def mount(_params, _session, socket) do
24 | if connected?(socket), do: :timer.send_interval(100, self(), :tick)
25 | {:ok, assign(socket, val: 72, mode: :cooling, time: NaiveDateTime.local_now())}
26 | end
27 |
28 | def handle_info(:tick, socket) do
29 | {:noreply, assign(socket, time: NaiveDateTime.local_now())}
30 | end
31 |
32 | def handle_event("inc", _, socket) do
33 | if socket.assigns.val >= 75, do: raise("boom")
34 | {:noreply, update(socket, :val, &(&1 + 1))}
35 | end
36 |
37 | def handle_event("dec", _, socket) do
38 | {:noreply, update(socket, :val, &(&1 - 1))}
39 | end
40 |
41 | def handle_event("toggle-mode", _, socket) do
42 | {:noreply,
43 | update(socket, :mode, fn
44 | :cooling -> :heating
45 | :heating -> :cooling
46 | end)}
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/lib/demo_web/live/user_live/index.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.UserLive.Index do
2 | use DemoWeb, :live_view
3 |
4 | alias Demo.Accounts
5 | alias DemoWeb.Router.Helpers, as: Routes
6 |
7 | def mount(_params, _session, socket) do
8 | if connected?(socket), do: Accounts.subscribe()
9 | {:ok, assign(socket, page: 1, per_page: 5)}
10 | end
11 |
12 | def handle_params(params, _url, socket) do
13 | {page, ""} = Integer.parse(params["page"] || "1")
14 | {:noreply, socket |> assign(page: page) |> fetch()}
15 | end
16 |
17 | defp fetch(socket) do
18 | %{page: page, per_page: per_page} = socket.assigns
19 | users = Accounts.list_users(page, per_page)
20 | assign(socket, users: users, page_title: "Listing Users – Page #{page}")
21 | end
22 |
23 | def handle_info({Accounts, [:user | _], _}, socket) do
24 | {:noreply, fetch(socket)}
25 | end
26 |
27 | def handle_event("keydown", %{"key" => "ArrowLeft"}, socket) do
28 | {:noreply, go_page(socket, socket.assigns.page - 1)}
29 | end
30 | def handle_event("keydown", %{"key" => "ArrowRight"}, socket) do
31 | {:noreply, go_page(socket, socket.assigns.page + 1)}
32 | end
33 | def handle_event("keydown", _, socket), do: {:noreply, socket}
34 |
35 | def handle_event("delete_user", %{"id" => id}, socket) do
36 | user = Accounts.get_user!(id)
37 | {:ok, _user} = Accounts.delete_user(user)
38 |
39 | {:noreply, socket}
40 | end
41 |
42 | defp go_page(socket, page) when page > 0 do
43 | push_patch(socket, to: Routes.user_index_path(socket, :index, page: page))
44 | end
45 | defp go_page(socket, _page), do: socket
46 | end
47 |
--------------------------------------------------------------------------------
/lib/demo_web/endpoint.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.Endpoint do
2 | use Phoenix.Endpoint, otp_app: :demo
3 |
4 | # The session will be stored in the cookie and signed,
5 | # this means its contents can be read but not tampered with.
6 | # Set :encryption_salt if you would also like to encrypt it.
7 | @session_options [
8 | store: :cookie,
9 | key: "_demo_key",
10 | signing_salt: "4nyFqBJ5"
11 | ]
12 |
13 | socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
14 |
15 | # Serve at "/" the static files from "priv/static" directory.
16 | #
17 | # You should set gzip to true if you are running phx.digest
18 | # when deploying your static files in production.
19 | plug Plug.Static,
20 | at: "/",
21 | from: :demo,
22 | gzip: false,
23 | only: ~w(assets fonts images favicon.ico robots.txt)
24 |
25 | # Code reloading can be explicitly enabled under the
26 | # :code_reloader configuration of your endpoint.
27 | if code_reloading? do
28 | socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
29 | plug Phoenix.LiveReloader
30 | plug Phoenix.CodeReloader
31 | plug Phoenix.Ecto.CheckRepoStatus, otp_app: :demo
32 | end
33 |
34 | plug Phoenix.LiveDashboard.RequestLogger,
35 | param_key: "request_logger",
36 | cookie_key: "request_logger"
37 |
38 | plug Plug.RequestId
39 | plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
40 |
41 | plug Plug.Parsers,
42 | parsers: [:urlencoded, :multipart, :json],
43 | pass: ["*/*"],
44 | json_decoder: Phoenix.json_library()
45 |
46 | plug Plug.MethodOverride
47 | plug Plug.Head
48 | plug Plug.Session, @session_options
49 | plug DemoWeb.Router
50 | end
51 |
--------------------------------------------------------------------------------
/test/support/data_case.ex:
--------------------------------------------------------------------------------
1 | defmodule Demo.DataCase do
2 | @moduledoc """
3 | This module defines the setup for tests requiring
4 | access to the application's data layer.
5 |
6 | You may define functions here to be used as helpers in
7 | your tests.
8 |
9 | Finally, if the test case interacts with the database,
10 | we enable the SQL sandbox, so changes done to the database
11 | are reverted at the end of every test. If you are using
12 | PostgreSQL, you can even run database tests asynchronously
13 | by setting `use Demo.DataCase, async: true`, although
14 | this option is not recommended for other databases.
15 | """
16 |
17 | use ExUnit.CaseTemplate
18 |
19 | using do
20 | quote do
21 | alias Demo.Repo
22 |
23 | import Ecto
24 | import Ecto.Changeset
25 | import Ecto.Query
26 | import Demo.DataCase
27 | end
28 | end
29 |
30 | setup tags do
31 | pid = Ecto.Adapters.SQL.Sandbox.start_owner!(Demo.Repo, shared: not tags[:async])
32 | on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
33 | :ok
34 | end
35 |
36 | @doc """
37 | A helper that transforms changeset errors into a map of messages.
38 |
39 | assert {:error, changeset} = Accounts.create_user(%{password: "short"})
40 | assert "password is too short" in errors_on(changeset).password
41 | assert %{password: ["password is too short"]} = errors_on(changeset)
42 |
43 | """
44 | def errors_on(changeset) do
45 | Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
46 | Regex.replace(~r"%{(\w+)}", message, fn _, key ->
47 | opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
48 | end)
49 | end)
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/demo_web/views/error_helpers.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.ErrorHelpers do
2 | @moduledoc """
3 | Conveniences for translating and building error messages.
4 | """
5 |
6 | use Phoenix.HTML
7 |
8 | @doc """
9 | Generates tag for inlined form input errors.
10 | """
11 | def error_tag(form, field) do
12 | Enum.map(Keyword.get_values(form.errors, field), fn error ->
13 | content_tag(:span, translate_error(error),
14 | class: "invalid-feedback",
15 | phx_feedback_for: input_name(form, field)
16 | )
17 | end)
18 | end
19 |
20 | @doc """
21 | Translates an error message using gettext.
22 | """
23 | def translate_error({msg, opts}) do
24 | # When using gettext, we typically pass the strings we want
25 | # to translate as a static argument:
26 | #
27 | # # Translate "is invalid" in the "errors" domain
28 | # dgettext("errors", "is invalid")
29 | #
30 | # # Translate the number of files with plural rules
31 | # dngettext("errors", "1 file", "%{count} files", count)
32 | #
33 | # Because the error messages we show in our forms and APIs
34 | # are defined inside Ecto, we need to translate them dynamically.
35 | # This requires us to call the Gettext module passing our gettext
36 | # backend as first argument.
37 | #
38 | # Note we use the "errors" domain, which means translations
39 | # should be written to the errors.po file. The :count option is
40 | # set by Ecto and indicates we should also apply plural rules.
41 | if count = opts[:count] do
42 | Gettext.dngettext(DemoWeb.Gettext, "errors", msg, msg, count, opts)
43 | else
44 | Gettext.dgettext(DemoWeb.Gettext, "errors", msg, opts)
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/config/config.exs:
--------------------------------------------------------------------------------
1 | # This file is responsible for configuring your application
2 | # and its dependencies with the aid of the Config module.
3 | #
4 | # This configuration file is loaded before any dependency and
5 | # is restricted to this project.
6 |
7 | # General application configuration
8 | import Config
9 |
10 | config :demo,
11 | ecto_repos: [Demo.Repo]
12 |
13 | # Configures the endpoint
14 | config :demo, DemoWeb.Endpoint,
15 | url: [host: "localhost"],
16 | secret_key_base: "TV0Hk+GbuJ5Ss2wuk/RUsZtuimE50C6YqwXkOKeBSQqTJkRmpXWUb3BCH71WxM+s",
17 | render_errors: [view: DemoWeb.ErrorView, accepts: ~w(html json), layout: false],
18 | pubsub_server: Demo.PubSub,
19 | live_view: [signing_salt: "iyK7AY6P"]
20 |
21 | # Configures the mailer
22 | #
23 | # By default it uses the "Local" adapter which stores the emails
24 | # locally. You can see the emails in your browser, at "/dev/mailbox".
25 | #
26 | # For production it's recommended to configure a different adapter
27 | # at the `config/runtime.exs`.
28 | config :demo, Demo.Mailer, adapter: Swoosh.Adapters.Local
29 |
30 | # Swoosh API client is needed for adapters other than SMTP.
31 | config :swoosh, :api_client, false
32 |
33 | # Configure esbuild (the version is required)
34 | config :esbuild,
35 | version: "0.12.18",
36 | default: [
37 | args: ~w(js/app.js --bundle --target=es2016 --outdir=../priv/static/assets),
38 | cd: Path.expand("../assets", __DIR__),
39 | env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
40 | ]
41 |
42 | # Configures Elixir's Logger
43 | config :logger, :console,
44 | format: "$time $metadata[$level] $message\n",
45 | metadata: [:request_id]
46 |
47 | # Use Jason for JSON parsing in Phoenix
48 | config :phoenix, :json_library, Jason
49 |
50 | # Import environment specific config. This must remain at the bottom
51 | # of this file so it overrides the configuration defined above.
52 | import_config "#{config_env()}.exs"
53 |
--------------------------------------------------------------------------------
/config/prod.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | # For production, don't forget to configure the url host
4 | # to something meaningful, Phoenix uses this information
5 | # when generating URLs.
6 | #
7 | # Note we also include the path to a cache manifest
8 | # containing the digested version of static files. This
9 | # manifest is generated by the `mix phx.digest` task,
10 | # which you should run after static files are built and
11 | # before starting your production server.
12 | config :demo, DemoWeb.Endpoint,
13 | url: [host: "example.com", port: 80],
14 | cache_static_manifest: "priv/static/cache_manifest.json"
15 |
16 | # Do not print debug messages in production
17 | config :logger, level: :info
18 |
19 | # ## SSL Support
20 | #
21 | # To get SSL working, you will need to add the `https` key
22 | # to the previous section and set your `:url` port to 443:
23 | #
24 | # config :demo, DemoWeb.Endpoint,
25 | # ...,
26 | # url: [host: "example.com", port: 443],
27 | # https: [
28 | # ...,
29 | # port: 443,
30 | # cipher_suite: :strong,
31 | # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
32 | # certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
33 | # ]
34 | #
35 | # The `cipher_suite` is set to `:strong` to support only the
36 | # latest and more secure SSL ciphers. This means old browsers
37 | # and clients may not be supported. You can set it to
38 | # `:compatible` for wider support.
39 | #
40 | # `:keyfile` and `:certfile` expect an absolute path to the key
41 | # and cert in disk or a relative path inside priv, for example
42 | # "priv/ssl/server.key". For all supported SSL configuration
43 | # options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1
44 | #
45 | # We also recommend setting `force_ssl` in your endpoint, ensuring
46 | # no data is ever sent via http, always redirecting to https:
47 | #
48 | # config :demo, DemoWeb.Endpoint,
49 | # force_ssl: [hsts: true]
50 | #
51 | # Check `Plug.SSL` for all available options in `force_ssl`.
52 |
--------------------------------------------------------------------------------
/lib/demo_web/live/page_live.ex:
--------------------------------------------------------------------------------
1 | defmodule DemoWeb.PageLive do
2 | use DemoWeb, :live_view
3 |
4 | def render(assigns) do
5 | ~H"""
6 |
7 |
<%= gettext "Welcome to %{name}!", name: "Phoenix LiveView" %>