Repository: phoenixframework/phoenix
Pull request #4598 (2021-11-13 09:32:31)

update controllers.md (safari no content type)
The docs are not precise, when using safari - as it will not render without a content_type..

More than open for better wording.

fixes https://github.com/phoenixframework/phoenix/issues/4597


Patch:
diff --git a/guides/controllers.md b/guides/controllers.md
index 0a69a443b..089283be7 100644
--- a/guides/controllers.md
+++ b/guides/controllers.md
@@ -262,9 +262,10 @@ def index(conn, _params) do
 end
 ```
 
-Reloading [http://localhost:4000](http://localhost:4000) should show us a completely blank page. The network tab of our browser's developer tools should show a response status of "201" (Created).
+Reloading [http://localhost:4000](http://localhost:4000) should show us a completely blank page. The network tab of our browser's developer tools should show a response status of "201" (Created). Some browsers (Safari) will download the response, as the content type is not set.
+
+To be specific about the content type, we can use [`put_resp_content_type/2`] in conjunction with [`send_resp/3`].
 
-If we would like to be really specific about the content type, we can use [`put_resp_content_type/2`] in conjunction with [`send_resp/3`].
 
 ```elixir
 def index(conn, _params) do
diff --git a/lib/mix/phoenix/schema.ex b/lib/mix/phoenix/schema.ex
index b01ba8f70..571ee90d4 100644
--- a/lib/mix/phoenix/schema.ex
+++ b/lib/mix/phoenix/schema.ex
@@ -286,7 +286,7 @@ defmodule Mix.Phoenix.Schema do
   defp type_to_default(key, t, :create) do
     case t do
         {:array, _}     -> []
-        {:enum, values} -> values |> translate_enum_vals() |> hd()
+        {:enum, values} -> build_enum_values(values, :create)
         :integer        -> 42
         :float          -> 120.5
         :decimal        -> "120.5"
@@ -307,7 +307,7 @@ defmodule Mix.Phoenix.Schema do
   defp type_to_default(key, t, :update) do
     case t do
         {:array, _}     -> []
-        {:enum, values} -> values |> translate_enum_vals() |> tl() |> hd()
+        {:enum, values} -> build_enum_values(values, :update)
         :integer        -> 43
         :float          -> 456.7
         :decimal        -> "456.7"
@@ -326,6 +326,14 @@ defmodule Mix.Phoenix.Schema do
     end
   end
 
+  defp build_enum_values(values, action) do
+    case {action, translate_enum_vals(values)} do
+      {:create, vals} -> hd(vals)
+      {:update, [val | []]} -> val
+      {:update, vals} -> vals |> tl() |> hd()
+    end
+  end
+
   defp build_utc_datetime_usec,
     do: %{DateTime.utc_now() | second: 0, microsecond: {0, 6}}
 
@@ -339,8 +347,8 @@ defmodule Mix.Phoenix.Schema do
     do: NaiveDateTime.truncate(build_utc_naive_datetime_usec(), :second)
 
 
-  @enum_missing_values_error """
-  Enum type requires at least two values
+  @enum_missing_value_error """
+  Enum type requires at least one value
   For example:
 
       mix phx.gen.schema Comment comments body:text status:enum:published:unpublished
@@ -355,25 +363,15 @@ defmodule Mix.Phoenix.Schema do
         mix phx.gen.schema Post posts settings:array:string
     """
   end
-  defp validate_attr!({_name, :enum}), do: Mix.raise @enum_missing_values_error
+  defp validate_attr!({_name, :enum}), do: Mix.raise @enum_missing_value_error
   defp validate_attr!({_name, type} = attr) when type in @valid_types, do: attr
-  defp validate_attr!({_name, {:enum, vals}} = attr), do: validate_enum_attr!(vals, attr)
+  defp validate_attr!({_name, {:enum, _vals}} = attr), do: attr
   defp validate_attr!({_name, {type, _}} = attr) when type in @valid_types, do: attr
   defp validate_attr!({_, type}) do
     Mix.raise "Unknown type `#{inspect type}` given to generator. " <>
               "The supported types are: #{@valid_types |> Enum.sort() |> Enum.join(", ")}"
   end
 
-  defp validate_enum_attr!(vals, attr) do
-     vals
-    |> Atom.to_string()
-    |> String.split(":")
-    |> case do
-      [_] -> Mix.raise @enum_missing_values_error
-      _ -> attr
-    end
-  end
-
   defp partition_attrs_and_assocs(schema_module, attrs) do
     {assocs, attrs} =
       Enum.split_with(attrs, fn
diff --git a/lib/phoenix/controller.ex b/lib/phoenix/controller.ex
index 2c79ed20c..e8155d84b 100644
--- a/lib/phoenix/controller.ex
+++ b/lib/phoenix/controller.ex
@@ -861,7 +861,7 @@ defmodule Phoenix.Controller do
   `Routes.some_route_path` helpers, as those are always relative.
   """
   def put_router_url(conn, %URI{} = uri) do
-    put_private(conn, :phoenix_router_url, uri)
+    put_private(conn, :phoenix_router_url, URI.to_string(uri))
   end
   def put_router_url(conn, url) when is_binary(url) do
     put_private(conn, :phoenix_router_url, url)
@@ -875,7 +875,7 @@ defmodule Phoenix.Controller do
   endpoint configuration (much like `put_router_url/2` but for static URLs).
   """
   def put_static_url(conn, %URI{} = uri) do
-    put_private(conn, :phoenix_static_url, uri)
+    put_private(conn, :phoenix_static_url, URI.to_string(uri))
   end
   def put_static_url(conn, url) when is_binary(url) do
     put_private(conn, :phoenix_static_url, url)
diff --git a/lib/phoenix/router/helpers.ex b/lib/phoenix/router/helpers.ex
index 443b8527e..7e65dfaaf 100644
--- a/lib/phoenix/router/helpers.ex
+++ b/lib/phoenix/router/helpers.ex
@@ -10,7 +10,6 @@ defmodule Phoenix.Router.Helpers do
   """
   def url(_router, %Conn{private: private}) do
     case private do
-      %{phoenix_router_url: %URI{} = uri} -> URI.to_string(uri)
       %{phoenix_router_url: url} when is_binary(url) -> url
       %{phoenix_endpoint: endpoint} -> endpoint.url()
     end
@@ -237,7 +236,6 @@ defmodule Phoenix.Router.Helpers do
       """
       def static_url(%Conn{private: private}, path) do
         case private do
-          %{phoenix_static_url: %URI{} = uri} -> URI.to_string(uri) <> path
           %{phoenix_static_url: url} when is_binary(url) -> url <> path
           %{phoenix_endpoint: endpoint} -> static_url(endpoint, path)
         end
@@ -418,13 +416,13 @@ defmodule Phoenix.Router.Helpers do
   end
 
   defp expand_segments([{:|, _, [h, t]}], acc),
-    do: quote(do: unquote(expand_segments([h], acc)) <> "/" <> Enum.map_join(unquote(t), "/", fn(s) -> URI.encode(s, &URI.char_unreserved?/1) end))
+    do: quote(do: unquote(expand_segments([h], acc)) <> "/" <> Enum.map_join(unquote(t), "/", &unquote(__MODULE__).encode_param/1))
 
   defp expand_segments([h|t], acc) when is_binary(h),
     do: expand_segments(t, quote(do: unquote(acc) <> unquote("/" <> h)))
 
   defp expand_segments([h|t], acc),
-    do: expand_segments(t, quote(do: unquote(acc) <> "/" <> URI.encode(to_param(unquote(h)), &URI.char_unreserved?/1)))
+    do: expand_segments(t, quote(do: unquote(acc) <> "/" <> unquote(__MODULE__).encode_param(to_param(unquote(h)))))
 
   defp expand_segments([], acc),
     do: acc


Test patch:
diff --git a/test/mix/tasks/phx.gen.html_test.exs b/test/mix/tasks/phx.gen.html_test.exs
index 72e2b3b3f..f22401e3d 100644
--- a/test/mix/tasks/phx.gen.html_test.exs
+++ b/test/mix/tasks/phx.gen.html_test.exs
@@ -32,13 +32,9 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do
         Gen.Html.run(~w(Blog Post))
       end
 
-      assert_raise Mix.Error, ~r/Enum type requires at least two values/, fn ->
+      assert_raise Mix.Error, ~r/Enum type requires at least one value/, fn ->
         Gen.Html.run(~w(Blog Post posts status:enum))
       end
-
-      assert_raise Mix.Error, ~r/Enum type requires at least two values/, fn ->
-        Gen.Html.run(~w(Blog Post posts status:enum:new))
-      end
     end
   end
 
@@ -421,5 +417,15 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do
         end
       end
     end
+
+    test "allows enum type with at least one value", config do
+      in_tmp_project config.test, fn ->
+        Gen.Html.run(~w(Blog Post posts status:enum:new))
+
+        assert_file "lib/phoenix_web/templates/post/form.html.heex", fn file ->
+          assert file =~ ~s|<%= select f, :status, Ecto.Enum.values(Phoenix.Blog.Post, :status), prompt: "Choose a value" %>|
+        end
+      end
+    end
   end
 end