Repository: phoenixframework/phoenix
Pull request #5431 (2023-04-21 08:43:21)

Update routing.md to replace MyApp with HelloWeb


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 22f49c5a1..3a0c4a57d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,7 +14,7 @@ Use the issues tracker for:
 Please **do not** use the issue tracker for personal support requests nor feature requests. Support requests should be sent to:
 
 * The [phoenix-talk mailing list](https://groups.google.com/group/phoenix-talk) (closed in favor of forum, but archive is still online)
-* [The Phoenix subforum on the Elixir forum](https://elixirforum.com/c/phoenix-forum)
+* [The Phoenix subforum on the Elixir Forum](https://elixirforum.com/c/phoenix-forum)
 * **[#elixir](irc://irc.libera.chat/elixir)** on [Libera](https://libera.chat/) IRC
 
 Development issues can be discussed on the [phoenix-core mailing list](https://groups.google.com/group/phoenix-core).
diff --git a/README.md b/README.md
index db46c1536..06e3b1ff3 100644
--- a/README.md
+++ b/README.md
@@ -83,7 +83,7 @@ npm install
 
   [1]: https://web.libera.chat/?channels=#elixir
   [2]: https://libera.chat/
-  [3]: https://elixir-slackin.herokuapp.com/
+  [3]: https://elixir-lang.slack.com/join/shared_invite/zt-1f13hz7mb-N4KGjF523ONLCcHfb8jYgA#/shared-invite/email
   [4]: https://github.com/phoenixframework/phoenix/issues
   [5]: https://elixirforum.com/c/phoenix-forum
   [6]: https://groups.google.com/group/phoenix-core
diff --git a/guides/deployment/gigalixir.md b/guides/deployment/gigalixir.md
index 5cdf45d50..48667955d 100644
--- a/guides/deployment/gigalixir.md
+++ b/guides/deployment/gigalixir.md
@@ -189,4 +189,4 @@ For custom domains, scaling, jobs and other features, see the [Gigalixir Documen
 
 See [Troubleshooting](https://gigalixir.readthedocs.io/en/latest/troubleshooting.html)
 
-Also, don't hesitate to email [help@gigalixir.com](mailto:help@gigalixir.com) or [request an invitation](https://elixir-slackin.herokuapp.com/) and join the #gigalixir channel on [Slack](https://elixir-lang.slack.com).
+Also, don't hesitate to email [help@gigalixir.com](mailto:help@gigalixir.com) or [request an invitation](https://elixir-lang.slack.com/join/shared_invite/zt-1f13hz7mb-N4KGjF523ONLCcHfb8jYgA#/shared-invite/email) and join the #gigalixir channel on [Slack](https://elixir-lang.slack.com).
diff --git a/guides/howto/using_ssl.md b/guides/howto/using_ssl.md
index 93183c75c..8af2f7456 100644
--- a/guides/howto/using_ssl.md
+++ b/guides/howto/using_ssl.md
@@ -4,7 +4,7 @@ To prepare an application to serve requests over SSL, we need to add a little bi
 
 The configuration consists of a new `https:` key for our endpoint whose value is a keyword list of port, path to the key file, and path to the cert (PEM) file. If we add the `otp_app:` key whose value is the name of our application, Plug will begin to look for them at the root of our application. We can then put those files in our `priv` directory and set the paths to `priv/our_keyfile.key` and `priv/our_cert.crt`.
 
-Here's an example configuration from `config/prod.exs`.
+Here's an example configuration from `config/runtime.exs`.
 
 ```elixir
 import Config
diff --git a/guides/introduction/community.md b/guides/introduction/community.md
index 9cc142369..b6f0b52a5 100644
--- a/guides/introduction/community.md
+++ b/guides/introduction/community.md
@@ -6,7 +6,7 @@ There are a number of places to connect with community members at all experience
 
   * We're on Libera IRC in the [\#elixir](https://web.libera.chat/?channels=#elixir) channel.
 
-  * [Request an invitation](https://elixir-slackin.herokuapp.com/) and join the #phoenix channel on [Slack](https://elixir-lang.slack.com).
+  * [Request an invitation](https://elixir-lang.slack.com/join/shared_invite/zt-1f13hz7mb-N4KGjF523ONLCcHfb8jYgA#/shared-invite/email) and join the #phoenix channel on [Slack](https://elixir-lang.slack.com).
 
   * Feel free to join and check out the #phoenix channel on [Discord](https://discord.gg/elixir).
 
diff --git a/guides/routing.md b/guides/routing.md
index 34bb02ef2..b2690a591 100644
--- a/guides/routing.md
+++ b/guides/routing.md
@@ -519,7 +519,7 @@ defmodule HelloWeb.Router do
 end
 ```
 
-The above assumes there is a plug called `MyApp.Authentication` that performs authentication and is now part of the `:auth` pipeline.
+The above assumes there is a plug called `HelloWeb.Authentication` that performs authentication and is now part of the `:auth` pipeline.
 
 Note that pipelines themselves are plugs, so we can plug a pipeline inside another pipeline. For example, we could rewrite the `review_checks` pipeline above to automatically invoke `browser`, simplifying the downstream pipeline call:
 
diff --git a/installer/templates/phx_web/controllers/page_html/home.html.heex b/installer/templates/phx_web/controllers/page_html/home.html.heex
index 3c8152790..3a5917f9f 100644
--- a/installer/templates/phx_web/controllers/page_html/home.html.heex
+++ b/installer/templates/phx_web/controllers/page_html/home.html.heex
@@ -159,22 +159,7 @@
               >
                 <path d="M8 13.833c3.866 0 7-2.873 7-6.416C15 3.873 11.866 1 8 1S1 3.873 1 7.417c0 1.081.292 2.1.808 2.995.606 1.05.806 2.399.086 3.375l-.208.283c-.285.386-.01.905.465.85.852-.098 2.048-.318 3.137-.81a3.717 3.717 0 0 1 1.91-.318c.263.027.53.041.802.041Z" />
               </svg>
-              Discuss on the Elixir forum
-            </a>
-          </div>
-          <div>
-            <a
-              href="https://elixir-slackin.herokuapp.com"
-              class="group -mx-2 -my-0.5 inline-flex items-center gap-3 rounded-lg px-2 py-0.5 hover:bg-zinc-50 hover:text-zinc-900"
-            >
-              <svg
-                viewBox="0 0 16 16"
-                aria-hidden="true"
-                class="h-4 w-4 fill-zinc-400 group-hover:fill-zinc-600"
-              >
-                <path d="M3.95 9.85a1.47 1.47 0 1 1-2.94 0 1.47 1.47 0 0 1 1.47-1.472h1.47v1.471Zm.735 0a1.47 1.47 0 1 1 2.94 0v3.678a1.47 1.47 0 1 1-2.94 0V9.85ZM6.156 3.942a1.47 1.47 0 0 1-1.47-1.472 1.47 1.47 0 1 1 2.94 0v1.472h-1.47Zm0 .747c.813 0 1.47.658 1.47 1.471a1.47 1.47 0 0 1-1.47 1.472H2.47A1.47 1.47 0 0 1 1 6.16 1.47 1.47 0 0 1 2.47 4.69h3.686ZM12.048 6.16a1.47 1.47 0 1 1 2.94 0 1.47 1.47 0 0 1-1.47 1.472h-1.47V6.16Zm-.735 0a1.47 1.47 0 1 1-2.94 0V2.47a1.47 1.47 0 1 1 2.94 0v3.69ZM9.843 12.057c.813 0 1.47.657 1.47 1.471a1.47 1.47 0 1 1-2.94 0v-1.471h1.47Zm0-.736a1.47 1.47 0 0 1-1.47-1.472 1.47 1.47 0 0 1 1.47-1.471h3.686c.813 0 1.47.658 1.47 1.471a1.47 1.47 0 0 1-1.47 1.472H9.843Z" />
-              </svg>
-              Join our Slack channel
+              Discuss on the Elixir Forum
             </a>
           </div>
           <div>
diff --git a/lib/phoenix/code_reloader.ex b/lib/phoenix/code_reloader.ex
index 9aeb034fe..931a7ccc5 100644
--- a/lib/phoenix/code_reloader.ex
+++ b/lib/phoenix/code_reloader.ex
@@ -30,17 +30,22 @@ defmodule Phoenix.CodeReloader do
   applications from library dependencies.
 
   This function is a no-op and returns `:ok` if Mix is not available.
+
+  ## Options
+
+    * `:reloadable_args` - additional CLI args to pass to the compiler tasks
+
   """
-  @spec reload(module) :: :ok | {:error, binary()}
-  def reload(endpoint) do
-    if Code.ensure_loaded?(Mix.Project), do: reload!(endpoint), else: :ok
+  @spec reload(module, keyword) :: :ok | {:error, binary()}
+  def reload(endpoint, opts \\ []) do
+    if Code.ensure_loaded?(Mix.Project), do: reload!(endpoint, opts), else: :ok
   end
 
   @doc """
   Same as `reload/1` but it will raise if Mix is not available.
   """
-  @spec reload!(module) :: :ok | {:error, binary()}
-  defdelegate reload!(endpoint), to: Phoenix.CodeReloader.Server
+  @spec reload!(module, keyword) :: :ok | {:error, binary()}
+  defdelegate reload!(endpoint, opts), to: Phoenix.CodeReloader.Server
 
   @doc """
   Synchronizes with the code server if it is alive.
diff --git a/lib/phoenix/code_reloader/proxy.ex b/lib/phoenix/code_reloader/proxy.ex
index fc203606f..2ada872bf 100644
--- a/lib/phoenix/code_reloader/proxy.ex
+++ b/lib/phoenix/code_reloader/proxy.ex
@@ -8,18 +8,26 @@ defmodule Phoenix.CodeReloader.Proxy do
     GenServer.start(__MODULE__, :ok)
   end
 
+  def diagnostics(proxy, diagnostics) do
+    GenServer.cast(proxy, {:diagnostics, diagnostics})
+  end
+
   def stop(proxy) do
-    GenServer.call(proxy, :stop)
+    GenServer.call(proxy, :stop, :infinity)
   end
 
   ## Callbacks
 
   def init(:ok) do
-    {:ok, ""}
+    {:ok, []}
+  end
+
+  def handle_cast({:diagnostics, diagnostics}, output) do
+    {:noreply, diagnostics |> Enum.map(&diagnostic_to_chars/1) |> Enum.reverse(output)}
   end
 
   def handle_call(:stop, _from, output) do
-    {:stop, :normal, output, output}
+    {:stop, :normal, Enum.reverse(output), output}
   end
 
   def handle_info(msg, output) do
@@ -47,6 +55,18 @@ defmodule Phoenix.CodeReloader.Proxy do
 
   defp put_chars(from, reply, chars, output) do
     send(Process.group_leader(), {:io_request, from, reply, {:put_chars, chars}})
-    {:noreply, output <> IO.chardata_to_string(chars)}
+    {:noreply, [chars | output]}
   end
+
+  defp diagnostic_to_chars(%{severity: :error, message: "**" <> _ = message}) do
+    "\n#{message}\n"
+  end
+
+  defp diagnostic_to_chars(%{severity: severity, message: message, file: file, position: position}) do
+    "\n#{severity}: #{message}\n  #{Path.relative_to_cwd(file)}#{position(position)}\n"
+  end
+
+  defp position({line, col}), do: ":#{line}:#{col}"
+  defp position(line) when is_integer(line) and line > 0, do: ":#{line}"
+  defp position(_), do: ""
 end
diff --git a/lib/phoenix/code_reloader/server.ex b/lib/phoenix/code_reloader/server.ex
index 64f467b46..525c1e637 100644
--- a/lib/phoenix/code_reloader/server.ex
+++ b/lib/phoenix/code_reloader/server.ex
@@ -13,8 +13,8 @@ defmodule Phoenix.CodeReloader.Server do
     GenServer.call(__MODULE__, :check_symlinks, :infinity)
   end
 
-  def reload!(endpoint) do
-    GenServer.call(__MODULE__, {:reload!, endpoint}, :infinity)
+  def reload!(endpoint, opts) do
+    GenServer.call(__MODULE__, {:reload!, endpoint, opts}, :infinity)
   end
 
   def sync do
@@ -58,9 +58,10 @@ defmodule Phoenix.CodeReloader.Server do
     {:reply, :ok, %{state | check_symlinks: false}}
   end
 
-  def handle_call({:reload!, endpoint}, from, state) do
+  def handle_call({:reload!, endpoint, opts}, from, state) do
     compilers = endpoint.config(:reloadable_compilers)
-    reloadable_apps = endpoint.config(:reloadable_apps) || default_reloadable_apps()
+    apps = endpoint.config(:reloadable_apps) || default_reloadable_apps()
+    args = Keyword.get(opts, :reloadable_args, [])
 
     # We do a backup of the endpoint in case compilation fails.
     # If so we can bring it back to finish the request handling.
@@ -70,7 +71,7 @@ defmodule Phoenix.CodeReloader.Server do
     {res, out} =
       proxy_io(fn ->
         try do
-          mix_compile(Code.ensure_loaded(Mix.Task), compilers, reloadable_apps, state.timestamp)
+          mix_compile(Code.ensure_loaded(Mix.Task), compilers, apps, args, state.timestamp)
         catch
           :exit, {:shutdown, 1} ->
             :error
@@ -159,7 +160,7 @@ defmodule Phoenix.CodeReloader.Server do
 
   defp all_waiting(acc, endpoint) do
     receive do
-      {:"$gen_call", from, {:reload!, ^endpoint}} -> all_waiting([from | acc], endpoint)
+      {:"$gen_call", from, {:reload!, ^endpoint, _}} -> all_waiting([from | acc], endpoint)
     after
       0 -> acc
     end
@@ -174,7 +175,7 @@ defmodule Phoenix.CodeReloader.Server do
     defp purge_protocols(_path), do: :ok
   end
 
-  defp mix_compile({:module, Mix.Task}, compilers, apps_to_reload, timestamp) do
+  defp mix_compile({:module, Mix.Task}, compilers, apps_to_reload, compile_args, timestamp) do
     config = Mix.Project.config()
     path = Mix.Project.consolidation_path(config)
 
@@ -183,8 +184,8 @@ defmodule Phoenix.CodeReloader.Server do
       purge_protocols(path)
     end
 
-    mix_compile_deps(Mix.Dep.cached(), apps_to_reload, compilers, timestamp, path)
-    mix_compile_project(config[:app], apps_to_reload, compilers, timestamp, path)
+    mix_compile_deps(Mix.Dep.cached(), apps_to_reload, compile_args, compilers, timestamp, path)
+    mix_compile_project(config[:app], apps_to_reload, compile_args, compilers, timestamp, path)
 
     if config[:consolidate_protocols] do
       Code.prepend_path(path)
@@ -193,30 +194,30 @@ defmodule Phoenix.CodeReloader.Server do
     :ok
   end
 
-  defp mix_compile({:error, _reason}, _, _, _) do
+  defp mix_compile({:error, _reason}, _, _, _, _) do
     raise "the Code Reloader is enabled but Mix is not available. If you want to " <>
             "use the Code Reloader in production or inside an escript, you must add " <>
             ":mix to your applications list. Otherwise, you must disable code reloading " <>
             "in such environments"
   end
 
-  defp mix_compile_deps(deps, apps_to_reload, compilers, timestamp, path) do
+  defp mix_compile_deps(deps, apps_to_reload, compile_args, compilers, timestamp, path) do
     for dep <- deps, dep.app in apps_to_reload do
       Mix.Dep.in_dependency(dep, fn _ ->
-        mix_compile_unless_stale_config(compilers, timestamp, path)
+        mix_compile_unless_stale_config(compilers, compile_args, timestamp, path)
       end)
     end
   end
 
-  defp mix_compile_project(nil, _, _, _, _), do: :ok
+  defp mix_compile_project(nil, _, _, _, _, _), do: :ok
 
-  defp mix_compile_project(app, apps_to_reload, compilers, timestamp, path) do
+  defp mix_compile_project(app, apps_to_reload, compile_args, compilers, timestamp, path) do
     if app in apps_to_reload do
-      mix_compile_unless_stale_config(compilers, timestamp, path)
+      mix_compile_unless_stale_config(compilers, compile_args, timestamp, path)
     end
   end
 
-  defp mix_compile_unless_stale_config(compilers, timestamp, path) do
+  defp mix_compile_unless_stale_config(compilers, compile_args, timestamp, path) do
     manifests = Mix.Tasks.Compile.Elixir.manifests()
     configs = Mix.Project.config_files()
     config = Mix.Project.config()
@@ -229,7 +230,7 @@ defmodule Phoenix.CodeReloader.Server do
           purge_modules(Path.join(Mix.Project.app_path(config), "ebin"))
         end
 
-        mix_compile(compilers, config, path)
+        mix_compile(compilers, compile_args, config, path)
 
       files ->
         raise """
@@ -243,7 +244,7 @@ defmodule Phoenix.CodeReloader.Server do
     end
   end
 
-  defp mix_compile(compilers, config, consolidation_path) do
+  defp mix_compile(compilers, compile_args, config, consolidation_path) do
     all = config[:compilers] || Mix.compilers()
 
     compilers =
@@ -255,7 +256,7 @@ defmodule Phoenix.CodeReloader.Server do
     # We call build_structure mostly for Windows so new
     # assets in priv are copied to the build directory.
     Mix.Project.build_structure(config)
-    args = ["--purge-consolidation-path-if-stale", consolidation_path]
+    args = ["--purge-consolidation-path-if-stale", consolidation_path | compile_args]
     result = run_compilers(compilers, args, [])
 
     cond do
@@ -301,8 +302,8 @@ defmodule Phoenix.CodeReloader.Server do
   defp run_compilers([compiler | compilers], args, acc) do
     with {status, diagnostics} <- Mix.Task.run("compile.#{compiler}", args) do
       # Diagnostics are written to stderr and therefore not captured,
-      # so we print them to the group leader here
-      Enum.each(diagnostics, &print_diagnostic/1)
+      # so we send them to the group leader here
+      Proxy.diagnostics(Process.group_leader(), diagnostics)
       {status, diagnostics}
     end
     |> case do
@@ -319,16 +320,4 @@ defmodule Phoenix.CodeReloader.Server do
       :noop
     end
   end
-
-  defp print_diagnostic(%{severity: :error, message: "**" <> _ = message}) do
-    IO.write("\n#{message}\n")
-  end
-
-  defp print_diagnostic(%{severity: severity, message: message, file: file, position: position}) do
-    IO.write("\n#{severity}: #{message}\n  #{Path.relative_to_cwd(file)}#{position(position)}\n")
-  end
-
-  defp position({line, col}), do: ":#{line}:#{col}"
-  defp position(line) when is_integer(line) and line > 0, do: ":#{line}"
-  defp position(_), do: ""
 end
diff --git a/lib/phoenix/controller.ex b/lib/phoenix/controller.ex
index 5cc2d1315..f2f9be3f6 100644
--- a/lib/phoenix/controller.ex
+++ b/lib/phoenix/controller.ex
@@ -307,7 +307,7 @@ defmodule Phoenix.Controller do
 
         def call(conn, {:error, :unauthorized}) do
           conn
-          |> put_status(403)
+          |> put_status(:forbidden)
           |> put_view(MyErrorView)
           |> render(:"403")
         end
diff --git a/lib/phoenix/socket/transport.ex b/lib/phoenix/socket/transport.ex
index e5a99a7ce..d2e8595bb 100644
--- a/lib/phoenix/socket/transport.ex
+++ b/lib/phoenix/socket/transport.ex
@@ -291,8 +291,12 @@ defmodule Phoenix.Socket.Transport do
   Runs the code reloader if enabled.
   """
   def code_reload(conn, endpoint, opts) do
-    reload? = Keyword.get(opts, :code_reloader, endpoint.config(:code_reloader))
-    reload? && Phoenix.CodeReloader.reload(endpoint)
+    if Keyword.get(opts, :code_reloader, endpoint.config(:code_reloader)) do
+      # If the WebSocket reconnects, then often the page has already been reloaded
+      # and we don't want to print warnings twice, so we disable all warnings.
+      Phoenix.CodeReloader.reload(endpoint, reloadable_args: ~w(--no-all-warnings))
+    end
+
     conn
   end
 


Test patch:
diff --git a/test/phoenix/code_reloader_test.exs b/test/phoenix/code_reloader_test.exs
index fc80efc2d..70b5d0cfe 100644
--- a/test/phoenix/code_reloader_test.exs
+++ b/test/phoenix/code_reloader_test.exs
@@ -3,13 +3,8 @@ defmodule Phoenix.CodeReloaderTest do
   use RouterHelper
 
   defmodule Endpoint do
-    def config(:reloadable_compilers) do
-      [:unknown_compiler, :elixir]
-    end
-
-    def config(:reloadable_apps) do
-      nil
-    end
+    def config(:reloadable_compilers), do: [:unknown_compiler, :elixir]
+    def config(:reloadable_apps), do: nil
   end
 
   def reload(_) do
@@ -48,7 +43,7 @@ defmodule Phoenix.CodeReloaderTest do
            |> Phoenix.CodeReloader.call(opts)
     assert conn.state == :unset
 
-    assert_receive {:trace, ^pid, :receive, {_, _, {:reload!, Endpoint}}}
+    assert_receive {:trace, ^pid, :receive, {_, _, {:reload!, Endpoint, _}}}
   end
 
   test "renders compilation error on failure" do