Repository: phoenixframework/phoenix
Pull request #3433 (2019-06-04 16:44:07)

Minor improvements to Channels docs page
I noticed some minor flaws while learning about Channels. 

- The js examples use [keyCode, which is deprecated](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode). I've updated the examples to use [key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) instead. 
- `li` elements are not valid children outside of ol/ul/menu elements ([src](https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element)). I've replaced the `li` with a `p` tag, which matches the referenced [phoenix chat example implementation](https://github.com/chrismccord/phoenix_chat_example/blob/master/web/static/js/app.js#L51).
- Lastly, I added some [aria live region attributes](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) for some very preliminary accessibility. This sort of feature would require a lot more work, but having some basic a11y functionality in the docs can hopefully provide a good start for developers.


Patch:
diff --git a/guides/channels.md b/guides/channels.md
index 13c35cc31..96c1759e0 100644
--- a/guides/channels.md
+++ b/guides/channels.md
@@ -144,14 +144,12 @@ The `Phoenix.Socket.Message` module defines a struct with the following keys whi
 
 ### PubSub
 
-Typically, we don't directly use the Phoenix PubSub layer when developing Phoenix applications.
-Rather, it's used internally by Phoenix itself.
-But we may need to configure it.
-
 PubSub consists of the `Phoenix.PubSub` module and a variety of modules for different adapters and their `GenServer`s.
 These modules contain functions which are the nuts and bolts of organizing Channel communication - subscribing to topics, unsubscribing from topics, and broadcasting messages on a topic.
+PubSub is used internally by Phoenix.
+It's also useful in application development in any case where you want to notify interested processes of an event; for instance, letting all connected [live views](https://github.com/phoenixframework/phoenix_live_view) know that a new comment has been added to a post.
 
-The PubSub system also takes care of getting messages from one node to another, so that it can be sent to all subscribers across the cluster.
+The PubSub system takes care of getting messages from one node to another so that they can be sent to all subscribers across the cluster.
 By default, this is done using [Phoenix.PubSub.PG2](https://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.PG2.html), which uses native BEAM messaging.
 
 If your deployment environment does not support distributed Elixir or direct communication between servers, Phoenix also ships with a [Redis Adapter](https://hexdocs.pm/phoenix_pubsub_redis/Phoenix.PubSub.Redis.html) that uses Redis to exchange PubSub data. Please see the [Phoenix.PubSub docs](http://hexdocs.pm/phoenix_pubsub/Phoenix.PubSub.html) for more information.
@@ -262,7 +260,7 @@ Save the file and your browser should auto refresh, thanks to the Phoenix live r
 In `lib/hello_web/templates/page/index.html.eex`, we'll replace the existing code with a container to hold our chat messages, and an input field to send them:
 
 ```html
-<div id="messages"></div>
+<div id="messages" role="log" aria-live="polite"></div>
 <input id="chat-input" type="text"></input>
 ```
 
@@ -275,7 +273,7 @@ let chatInput         = document.querySelector("#chat-input")
 let messagesContainer = document.querySelector("#messages")
 
 chatInput.addEventListener("keypress", event => {
-  if(event.keyCode === 13){
+  if(event.key === 'Enter'){
     channel.push("new_msg", {body: chatInput.value})
     chatInput.value = ""
   }
@@ -297,14 +295,14 @@ let chatInput         = document.querySelector("#chat-input")
 let messagesContainer = document.querySelector("#messages")
 
 chatInput.addEventListener("keypress", event => {
-  if(event.keyCode === 13){
+  if(event.key === 'Enter'){
     channel.push("new_msg", {body: chatInput.value})
     chatInput.value = ""
   }
 })
 
 channel.on("new_msg", payload => {
-  let messageItem = document.createElement("li")
+  let messageItem = document.createElement("p")
   messageItem.innerText = `[${Date()}] ${payload.body}`
   messagesContainer.appendChild(messageItem)
 })
diff --git a/guides/contexts.md b/guides/contexts.md
index 45a72887f..facd9434b 100644
--- a/guides/contexts.md
+++ b/guides/contexts.md
@@ -1096,8 +1096,8 @@ defmodule Hello.UserRegistration do
 
   def register_user(params) do
     Multi.new()
-    |> Multi.run(:user, fn _ -> Accounts.create_user(params) end)
-    |> Multi.run(:author, fn %{user: user} ->
+    |> Multi.run(:user, fn _repo, _ -> Accounts.create_user(params) end)
+    |> Multi.run(:author, fn _repo, %{user: user} ->
       {:ok, CMS.ensure_author_exists(user)}
     end)
     |> Repo.transaction()
diff --git a/guides/ecto.md b/guides/ecto.md
index 1e8bada7b..e553fed25 100644
--- a/guides/ecto.md
+++ b/guides/ecto.md
@@ -2,11 +2,10 @@
 
 Most web applications today need some form of data validation and persistence. In the Elixir ecosystem, we have Ecto to enable this. Before we jump into building database-backed web features, we're going to focus on the finer details of Ecto to give a solid base to build our web features on top of. Let's get started!
 
-Ecto currently has adapters for the following databases:
+Ecto has out of the box support for the following databases:
 
-* PostgreSQL
-* MySQL
-* MSSQL
+* PostgreSQL (via [`postgrex`](https://github.com/elixir-ecto/postgrex))
+* MySQL (via [`myxql`](https://github.com/elixir-ecto/myxql))
 
 Newly generated Phoenix projects include Ecto with the PostgreSQL adapter by default (you can pass the `--no-ecto` flag to exclude this).
 
diff --git a/guides/presence.md b/guides/presence.md
index 4569107fe..a993ac30e 100644
--- a/guides/presence.md
+++ b/guides/presence.md
@@ -64,7 +64,7 @@ end
 We also need to change our connect function to take a `user_id` from the params and assign it on the socket. In production you may want to use `Phoenix.Token` if you have real users that are authenticated.
 
 ```elixir
-def connect(params, socket) do
+def connect(params, socket, _connect_info) do
   {:ok, assign(socket, :user_id, params["user_id"])}
 end
 ```
diff --git a/guides/up_and_running.md b/guides/up_and_running.md
index 550b53222..5c6bc4816 100644
--- a/guides/up_and_running.md
+++ b/guides/up_and_running.md
@@ -16,7 +16,7 @@ $ mix phx.new hello
 
 > A note about [webpack](https://webpack.js.org/) before we begin: Phoenix will use webpack for asset management by default. Webpack's dependencies are installed via the node package manager, not mix. Phoenix will prompt us to install them at the end of the `mix phx.new` task. If we say "no" at that point, and if we don't install those dependencies later with `npm install`, our application will raise errors when we try to start it, and our assets may not load properly. If we don't want to use webpack at all, we can simply pass `--no-webpack` to `mix phx.new`.
 
-> A note about [Ecto](https://hexdocs.pm/phoenix/ecto.html): Ecto allows our Phoenix application to communicate with a data store, such as PostgreSQL, MySQL, or MSSQL. If our application will not require this component we can skip this dependency by passing the `--no-ecto` flag to `mix phx.new`. This flag may also be combined with `--no-webpack` to create a skeleton application.
+> A note about [Ecto](https://hexdocs.pm/phoenix/ecto.html): Ecto allows our Phoenix application to communicate with a data store, such as PostgreSQL, MySQL, and others. If our application will not require this component we can skip this dependency by passing the `--no-ecto` flag to `mix phx.new`. This flag may also be combined with `--no-webpack` to create a skeleton application.
 
 > To learn more about `mix phx.new` you can read the [Mix Tasks Guide](phoenix_mix_tasks.html#phoenix-specific-mix-tasks).
 
diff --git a/installer/templates/phx_assets/webpack.config.js b/installer/templates/phx_assets/webpack.config.js
index 40b8fa684..ad2083175 100644
--- a/installer/templates/phx_assets/webpack.config.js
+++ b/installer/templates/phx_assets/webpack.config.js
@@ -13,7 +13,7 @@ module.exports = (env, options) => ({
     ]
   },
   entry: {
-    './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js'))
+    './js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
   },
   output: {
     filename: 'app.js',
diff --git a/lib/mix/tasks/compile.phoenix.ex b/lib/mix/tasks/compile.phoenix.ex
index f9078442e..ff7ce5240 100644
--- a/lib/mix/tasks/compile.phoenix.ex
+++ b/lib/mix/tasks/compile.phoenix.ex
@@ -11,8 +11,8 @@ defmodule Mix.Tasks.Compile.Phoenix do
     {:ok, _} = Application.ensure_all_started(:phoenix)
 
     case touch() do
-      [] -> :noop
-      _  -> :ok
+      [] -> {:noop, []}
+      _  -> {:ok, []}
     end
   end
 
diff --git a/lib/phoenix/config.ex b/lib/phoenix/config.ex
index ea983c4a5..1aff40c40 100644
--- a/lib/phoenix/config.ex
+++ b/lib/phoenix/config.ex
@@ -12,7 +12,7 @@ defmodule Phoenix.Config do
   @doc """
   Starts a Phoenix configuration handler.
   """
-  def start_link(module, config, defaults, opts \\ []) do
+  def start_link({module, config, defaults, opts}) do
     permanent = Keyword.keys(defaults)
     GenServer.start_link(__MODULE__, {module, config, permanent}, opts)
   end
diff --git a/lib/phoenix/endpoint/supervisor.ex b/lib/phoenix/endpoint/supervisor.ex
index 4bdbc9f04..60d55732f 100644
--- a/lib/phoenix/endpoint/supervisor.ex
+++ b/lib/phoenix/endpoint/supervisor.ex
@@ -67,8 +67,7 @@ defmodule Phoenix.Endpoint.Supervisor do
       server_children(mod, conf, otp_app, server?) ++
       watcher_children(mod, conf, server?)
 
-    # Supervisor.init(children, strategy: :one_for_one)
-    {:ok, {{:one_for_one, 3, 5}, children}}
+    Supervisor.init(children, strategy: :one_for_one)
   end
 
   defp pubsub_children(_mod, conf) do
@@ -89,8 +88,8 @@ defmodule Phoenix.Endpoint.Supervisor do
   end
 
   defp config_children(mod, conf, default_conf) do
-    args = [mod, conf, default_conf, [name: Module.concat(mod, "Config")]]
-    [worker(Phoenix.Config, args)]
+    args = {mod, conf, default_conf, name: Module.concat(mod, "Config")}
+    [{Phoenix.Config, args}]
   end
 
   defp server_children(mod, config, otp_app, server?) do
@@ -163,8 +162,7 @@ defmodule Phoenix.Endpoint.Supervisor do
   defp watcher_children(_mod, conf, server?) do
     if server? do
       Enum.map(conf[:watchers], fn {cmd, args} ->
-        worker(Phoenix.Endpoint.Watcher, watcher_args(cmd, args),
-               id: make_ref(), restart: :transient)
+        {Phoenix.Endpoint.Watcher, watcher_args(cmd, args)}
       end)
     else
       []
@@ -173,7 +171,7 @@ defmodule Phoenix.Endpoint.Supervisor do
 
   defp watcher_args(cmd, cmd_args) do
     {args, opts} = Enum.split_while(cmd_args, &is_binary(&1))
-    [cmd, args, opts]
+    {cmd, args, opts}
   end
 
   @doc """
diff --git a/lib/phoenix/endpoint/watcher.ex b/lib/phoenix/endpoint/watcher.ex
index d78256794..386318d01 100644
--- a/lib/phoenix/endpoint/watcher.ex
+++ b/lib/phoenix/endpoint/watcher.ex
@@ -1,10 +1,16 @@
 defmodule Phoenix.Endpoint.Watcher do
   @moduledoc false
-  use Task
-
   require Logger
 
-  def start_link(cmd, args, opts) do
+  def child_spec(args) do
+    %{
+      id: make_ref(),
+      start: {__MODULE__, :start_link, [args]},
+      restart: :transient
+    }
+  end
+
+  def start_link({cmd, args, opts}) do
     Task.start_link(__MODULE__, :watch, [to_string(cmd), args, opts])
   end
 
diff --git a/lib/phoenix/token.ex b/lib/phoenix/token.ex
index 9226e9029..f89c89d33 100644
--- a/lib/phoenix/token.ex
+++ b/lib/phoenix/token.ex
@@ -69,7 +69,7 @@ defmodule Phoenix.Token do
       defmodule MyApp.UserSocket do
         use Phoenix.Socket
 
-        def connect(%{"token" => token}, socket) do
+        def connect(%{"token" => token}, socket, _connect_info) do
           case Phoenix.Token.verify(socket, "user salt", token, max_age: 86400) do
             {:ok, user_id} ->
               socket = assign(socket, :user, Repo.get!(User, user_id))
@@ -78,6 +78,8 @@ defmodule Phoenix.Token do
               :error
           end
         end
+
+        def connect(_params, _socket, _connect_info), do: :error
       end
 
   In this example, the phoenix.js client will send the token in the
diff --git a/priv/templates/phx.gen.context/schema_access.ex b/priv/templates/phx.gen.context/schema_access.ex
index f75618931..769889156 100644
--- a/priv/templates/phx.gen.context/schema_access.ex
+++ b/priv/templates/phx.gen.context/schema_access.ex
@@ -88,7 +88,7 @@
   ## Examples
 
       iex> change_<%= schema.singular %>(<%= schema.singular %>)
-      %Ecto.Changeset{source: %<%= inspect schema.alias %>{}}
+      %Ecto.Changeset{data: %<%= inspect schema.alias %>{}}
 
   """
   def change_<%= schema.singular %>(%<%= inspect schema.alias %>{} = <%= schema.singular %>) do


Test patch:
diff --git a/test/mix/tasks/phx.new_test.exs b/test/mix/tasks/phx.new_test.exs
index 3f8bcf684..68e6aadd8 100644
--- a/test/mix/tasks/phx.new_test.exs
+++ b/test/mix/tasks/phx.new_test.exs
@@ -10,7 +10,7 @@ for pattern <- ["../../../installer/lib/phx_new/project.ex",
 # Define a fake live reload socket.
 defmodule Phoenix.LiveReloader.Socket do
   def child_spec(_) do
-    Supervisor.Spec.worker(Task, [fn -> :ok end], restart: :temporary)
+    Supervisor.child_spec({Task, fn -> :ok end}, [])
   end
 end
 
diff --git a/test/phoenix/code_reloader_test.exs b/test/phoenix/code_reloader_test.exs
index 41fed3343..4f3e3e53e 100644
--- a/test/phoenix/code_reloader_test.exs
+++ b/test/phoenix/code_reloader_test.exs
@@ -17,7 +17,7 @@ defmodule Phoenix.CodeReloaderTest do
   end
 
   test "compile.phoenix tasks touches files" do
-    assert Mix.Tasks.Compile.Phoenix.run([]) == :noop
+    assert Mix.Tasks.Compile.Phoenix.run([]) == {:noop, []}
   end
 
   test "reloads on every request" do
diff --git a/test/phoenix/config_test.exs b/test/phoenix/config_test.exs
index af2ff8cf8..e447010a9 100644
--- a/test/phoenix/config_test.exs
+++ b/test/phoenix/config_test.exs
@@ -15,7 +15,7 @@ defmodule Phoenix.ConfigTest do
   end
 
   test "starts an ets table as part of the module", meta do
-    {:ok, _pid} = start_link(meta.test, @all, @defaults)
+    {:ok, _pid} = start_link({meta.test, @all, @defaults, []})
     assert :ets.info(meta.test, :name) == meta.test
     assert :ets.lookup(meta.test, :parsers) == [parsers: false]
     assert :ets.lookup(meta.test, :static)  == [static: [at: "/"]]
@@ -23,7 +23,7 @@ defmodule Phoenix.ConfigTest do
   end
 
   test "can change configuration", meta do
-    {:ok, _pid} = start_link(meta.test, @all, @defaults)
+    {:ok, _pid} = start_link({meta.test, @all, @defaults, []})
 
     # Nothing changed
     config_change(meta.test, [], [])
@@ -43,7 +43,7 @@ defmodule Phoenix.ConfigTest do
   end
 
   test "can cache", meta do
-    {:ok, _pid} = start_link(meta.test, @all, @defaults)
+    {:ok, _pid} = start_link({meta.test, @all, @defaults, []})
 
     assert cache(meta.test, :__hello__, fn _ -> {:nocache, 1} end) == 1
     assert cache(meta.test, :__hello__, fn _ -> {:cache, 2} end) == 2
diff --git a/test/phoenix/endpoint/watcher_test.exs b/test/phoenix/endpoint/watcher_test.exs
index 6f6fb6232..e1a85aefb 100644
--- a/test/phoenix/endpoint/watcher_test.exs
+++ b/test/phoenix/endpoint/watcher_test.exs
@@ -6,7 +6,7 @@ defmodule Phoenix.Endpoint.WatcherTest do
 
   test "starts watching and writes to stdio" do
     assert capture_io(fn ->
-      {:ok, pid} = Watcher.start_link("echo", ["hello"], cd: File.cwd!())
+      {:ok, pid} = Watcher.start_link({"echo", ["hello"], cd: File.cwd!()})
       ref = Process.monitor(pid)
       assert_receive {:DOWN, ^ref, :process, ^pid, :normal}, 1000
     end) == "hello\n"
diff --git a/test/phoenix/integration/long_poll_channels_test.exs b/test/phoenix/integration/long_poll_channels_test.exs
index c8d4a9af4..3abf0ec3a 100644
--- a/test/phoenix/integration/long_poll_channels_test.exs
+++ b/test/phoenix/integration/long_poll_channels_test.exs
@@ -133,8 +133,8 @@ defmodule Phoenix.Integration.LongPollChannelsTest do
   end
 
   setup config do
-    for {_, pid, _, _} <- Supervisor.which_children(Phoenix.Transports.LongPoll.Supervisor) do
-      Supervisor.terminate_child(Phoenix.Transports.LongPoll.Supervisor, pid)
+    for {_, pid, _, _} <- DynamicSupervisor.which_children(Phoenix.Transports.LongPoll.Supervisor) do
+      DynamicSupervisor.terminate_child(Phoenix.Transports.LongPoll.Supervisor, pid)
     end
 
     {:ok, topic: "room:" <> to_string(config.test)}
diff --git a/test/phoenix/integration/long_poll_socket_test.exs b/test/phoenix/integration/long_poll_socket_test.exs
index f3cc39b0c..8e1aefd08 100644
--- a/test/phoenix/integration/long_poll_socket_test.exs
+++ b/test/phoenix/integration/long_poll_socket_test.exs
@@ -27,7 +27,7 @@ defmodule Phoenix.Integration.LongPollSocketTest do
 
     def child_spec(opts) do
       :value = Keyword.fetch!(opts, :custom)
-      Supervisor.Spec.worker(Task, [fn -> :ok end], restart: :transient)
+      Supervisor.child_spec({Task, fn -> :ok end}, [])
     end
 
     def connect(map) do
@@ -77,8 +77,8 @@ defmodule Phoenix.Integration.LongPollSocketTest do
   end
 
   setup do
-    for {_, pid, _, _} <- Supervisor.which_children(Phoenix.Transports.LongPoll.Supervisor) do
-      Supervisor.terminate_child(Phoenix.Transports.LongPoll.Supervisor, pid)
+    for {_, pid, _, _} <- DynamicSupervisor.which_children(Phoenix.Transports.LongPoll.Supervisor) do
+      DynamicSupervisor.terminate_child(Phoenix.Transports.LongPoll.Supervisor, pid)
     end
 
     :ok
diff --git a/test/phoenix/integration/websocket_socket_test.exs b/test/phoenix/integration/websocket_socket_test.exs
index c0fb5d558..c5fe44d4a 100644
--- a/test/phoenix/integration/websocket_socket_test.exs
+++ b/test/phoenix/integration/websocket_socket_test.exs
@@ -25,7 +25,7 @@ defmodule Phoenix.Integration.WebSocketTest do
 
     def child_spec(opts) do
       :value = Keyword.fetch!(opts, :custom)
-      Supervisor.Spec.worker(Task, [fn -> :ok end], restart: :transient)
+      Supervisor.child_spec({Task, fn -> :ok end}, [])
     end
 
     def connect(map) do