Repository: phoenixframework/phoenix
Pull request #2962 (2018-07-14 04:15:05)

Fix incorrect `:halt` key name in the guides
While the function is named `Plug.Conn.halt/1`, the key that is updated is `:halted`. I believe this is a typo in the guides.


Patch:
diff --git a/guides/channels.md b/guides/channels.md
index 182ae923f..5fb0b0992 100644
--- a/guides/channels.md
+++ b/guides/channels.md
@@ -401,7 +401,7 @@ Next we need to pass this token to JavaScript. We can do so inside a script tag
 
 ```html
 <script>window.userToken = "<%= assigns[:user_token] %>";</script>
-<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
+<script src="<%= Routes.static_url(@conn, "/js/app.js") %>"></script>
 ```
 
 **Step 3 - Pass the Token to the Socket Constructor and Verify**
diff --git a/guides/contexts.md b/guides/contexts.md
index d1776e1db..1ef674e47 100644
--- a/guides/contexts.md
+++ b/guides/contexts.md
@@ -29,7 +29,7 @@ Before we use the generators, we need to undo the changes we made in the Ecto gu
 
 ```console
 $ rm lib/hello/user.ex
-$ rm priv/repo/migrations/*_create_user.exs
+$ rm priv/repo/migrations/*_create_users.exs
 ```
 
 Next, let's reset our database so we also discard the table we have just removed:
@@ -147,7 +147,7 @@ defmodule HelloWeb.UserController do
       {:ok, user} ->
         conn
         |> put_flash(:info, "User created successfully.")
-        |> redirect(to: user_path(conn, :show, user))
+        |> redirect(to: Routes.user_path(conn, :show, user))
       {:error, %Ecto.Changeset{} = changeset} ->
         render(conn, "new.html", changeset: changeset)
     end
@@ -312,8 +312,7 @@ Before we integrate credentials in the web layer, we need to let our context kno
 
 
 ```elixir
-- alias Hello.Accounts.User
-+ alias Hello.Accounts.{User, Credential}
++ alias Hello.Accounts.Credential
 
 
   schema "users" do
@@ -330,8 +329,7 @@ Before we integrate credentials in the web layer, we need to let our context kno
 We used `Ecto.Schema`'s `has_one` macro to let Ecto know how to associate our parent User to a child Credential. Next, let's add the relationships in the opposite direction in `accounts/credential.ex`:
 
 ```elixir
-- alias Hello.Accounts.Credential
-+ alias Hello.Accounts.{Credential, User}
++ alias Hello.Accounts.User
 
 
   schema "credentials" do
@@ -480,7 +478,7 @@ defmodule HelloWeb.SessionController do
       {:error, :unauthorized} ->
         conn
         |> put_flash(:error, "Bad email/password combination")
-        |> redirect(to: session_path(conn, :new))
+        |> redirect(to: Routes.session_path(conn, :new))
     end
   end
 
@@ -540,7 +538,7 @@ Next, add a new template in `lib/hello_web/templates/session/new.html.eex:`
 ```eex
 <h1>Sign in</h1>
 
-<%= form_for @conn, session_path(@conn, :create), [method: :post, as: :user], fn f -> %>
+<%= form_for @conn, Routes.session_path(@conn, :create), [method: :post, as: :user], fn f -> %>
   <div class="form-group">
     <%= text_input f, :email, placeholder: "Email" %>
   </div>
@@ -554,7 +552,7 @@ Next, add a new template in `lib/hello_web/templates/session/new.html.eex:`
   </div>
 <% end %>
 
-<%= form_for @conn, session_path(@conn, :delete), [method: :delete, as: :user], fn _ -> %>
+<%= form_for @conn, Routes.session_path(@conn, :delete), [method: :delete, as: :user], fn _ -> %>
   <div class="form-group">
     <%= submit "logout" %>
   </div>
@@ -775,8 +773,7 @@ Now that we know where our data dependencies exist, let's add our schema associa
 
 
 ```elixir
-- alias Hello.CMS.Page
-+ alias Hello.CMS.{Page, Author}
++ alias Hello.CMS.Author
 
 
   schema "pages" do
@@ -795,8 +792,7 @@ Next, let's add the association in the other direction in `lib/hello/cms/author.
 
 ```elixir
 
-- alias Hello.CMS.Author
-+ alias Hello.CMS.{Author, Page}
++ alias Hello.CMS.Page
 
 
   schema "authors" do
@@ -893,7 +889,7 @@ Open up your generated `lib/hello_web/controllers/cms/page_controller.ex` and ma
     else
       conn
       |> put_flash(:error, "You can't modify that page")
-      |> redirect(to: cms_page_path(conn, :index))
+      |> redirect(to: Routes.cms_page_path(conn, :index))
       |> halt()
     end
   end
@@ -921,7 +917,7 @@ With our new plugs in place, we can now modify our `create`, `edit`, `update`, a
       {:ok, page} ->
         conn
         |> put_flash(:info, "Page created successfully.")
-        |> redirect(to: cms_page_path(conn, :show, page))
+        |> redirect(to: Routes.cms_page_path(conn, :show, page))
       {:error, %Ecto.Changeset{} = changeset} ->
         render(conn, "new.html", changeset: changeset)
     end
@@ -935,7 +931,7 @@ With our new plugs in place, we can now modify our `create`, `edit`, `update`, a
       {:ok, page} ->
         conn
         |> put_flash(:info, "Page updated successfully.")
-        |> redirect(to: cms_page_path(conn, :show, page))
+        |> redirect(to: Routes.cms_page_path(conn, :show, page))
       {:error, %Ecto.Changeset{} = changeset} ->
 -       render(conn, "edit.html", page: page, changeset: changeset)
 +       render(conn, "edit.html", changeset: changeset)
@@ -950,7 +946,7 @@ With our new plugs in place, we can now modify our `create`, `edit`, `update`, a
 
     conn
     |> put_flash(:info, "Page deleted successfully.")
-    |> redirect(to: cms_page_path(conn, :index))
+    |> redirect(to: Routes.cms_page_path(conn, :index))
   end
 ```
 
diff --git a/guides/controllers.md b/guides/controllers.md
index 2d5dda81f..d808a7990 100644
--- a/guides/controllers.md
+++ b/guides/controllers.md
@@ -518,7 +518,7 @@ defmodule HelloWeb.PageController do
   use HelloWeb, :controller
 
   def index(conn, _params) do
-    redirect(conn, to: redirect_test_path(conn, :redirect_test))
+    redirect(conn, to: Routes.redirect_test_path(conn, :redirect_test))
   end
 end
 ```
@@ -527,7 +527,7 @@ Note that we can't use the url helper here because `redirect/2` using the atom `
 
 ```elixir
 def index(conn, _params) do
-  redirect(conn, to: redirect_test_url(conn, :redirect_test))
+  redirect(conn, to: Routes.redirect_test_url(conn, :redirect_test))
 end
 ```
 
@@ -535,7 +535,7 @@ If we want to use the url helper to pass a full url to `redirect/2`, we must use
 
 ```elixir
 def index(conn, _params) do
-  redirect(conn, external: redirect_test_url(conn, :redirect_test))
+  redirect(conn, external: Routes.redirect_test_url(conn, :redirect_test))
 end
 ```
 
@@ -670,7 +670,7 @@ It's important to note that `halt/1` simply sets the `:halted` key on `Plug.Conn
   |> send_resp(404, "Not found")
 ```
 
-It's also important to note that halting will only stop the plug pipeline from continuing. Function plugs will still execute unless their implementation checks for the `:halt` value.
+It's also important to note that halting will only stop the plug pipeline from continuing. Function plugs will still execute unless their implementation checks for the `:halted` value.
 
 ```
   def post_authorization_plug(%{halted: true} = conn, _), do: conn
diff --git a/guides/ecto.md b/guides/ecto.md
index 4590a0a5f..569637154 100644
--- a/guides/ecto.md
+++ b/guides/ecto.md
@@ -31,7 +31,7 @@ $ mix phx.gen.schema User users name:string email:string \
 bio:string number_of_pets:integer
 
 * creating ./lib/hello/user.ex
-* creating priv/repo/migrations/20170523151118_create_user.exs
+* creating priv/repo/migrations/20170523151118_create_users.exs
 
 Remember to update your repository by running migrations:
 
@@ -47,7 +47,7 @@ $ mix ecto.migrate
 Compiling 1 file (.ex)
 Generated hello app
 
-[info]  == Running Hello.Repo.Migrations.CreateHello.User.change/0 forward
+[info]  == Running Hello.Repo.Migrations.CreateUsers.change/0 forward
 
 [info]  create table users
 
@@ -79,7 +79,7 @@ hello_dev=# \q
 If we take a look at the migration generated by `phx.gen.schema` in `priv/repo/migrations`, we'll see that it will add the columns we specified. It will also add timestamp columns for `inserted_at` and `updated_at` which come from the `timestamps/0` function.
 
 ```elixir
-defmodule Hello.Repo.Migrations.CreateHello.User do
+defmodule Hello.Repo.Migrations.CreateUsers do
   use Ecto.Migration
 
   def change do
@@ -515,4 +515,4 @@ $ mix ecto.migrate
 [info] == Running HelloPhoenix.Repo.Migrations.CreateUser.change/0 forward
 [info] create table users
 [info] == Migrated in 0.2s
-```
\ No newline at end of file
+```
diff --git a/guides/phoenix_mix_tasks.md b/guides/phoenix_mix_tasks.md
index a4a2f139c..3cacf7d15 100644
--- a/guides/phoenix_mix_tasks.md
+++ b/guides/phoenix_mix_tasks.md
@@ -225,17 +225,14 @@ Remember to update your repository by running migrations:
     $ mix ecto.migrate
 ```
 
-Important: If we don't do this, our application won't compile, and we'll get an error.
+Important: If we don't do this, we will see the following warnings in our logs, and our application will error when trying to execute the function.
 
 ```console
 $ mix phx.server
 Compiling 17 files (.ex)
 
-== Compilation error in file lib/hello_web/controllers/post_controller.ex ==
-** (CompileError) lib/hello_web/controllers/post_controller.ex:22: undefined function post_path/3
-    (stdlib) lists.erl:1338: :lists.foreach/2
-    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
-    (elixir) lib/kernel/parallel_compiler.ex:121: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
+warning: function HelloWeb.Router.Helpers.post_path/3 is undefined or private
+  lib/hello_web/controllers/post_controller.ex:22: 
 ```
 
 If we don't want to create a context or schema for our resource we can use the `--no-context` flag. Note that this still requires a context module name as a parameter.
@@ -260,17 +257,14 @@ Add the resource to your browser scope in lib/hello_web/router.ex:
     resources "/posts", PostController
 ```
 
-Important: If we don't do this, our application won't compile, and we'll get an error.
+Important: If we don't do this, we'll get the following warning in our logs and the application will error when attempting to load the page:
 
 ```console
 $ mix phx.server
 Compiling 15 files (.ex)
 
-== Compilation error in file lib/hello_web/views/post_view.ex ==
-** (CompileError) lib/hello_web/templates/post/edit.html.eex:3: undefined function post_path/3
-    (stdlib) lists.erl:1338: :lists.foreach/2
-    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
-    (elixir) lib/kernel/parallel_compiler.ex:121: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
+warning: function HelloWeb.Router.Helpers.post_path/3 is undefined or private
+  lib/hello_web/templates/post/edit.html.eex:3
 ```
 
 Similarly - if we want a context created without a schema for our resource we can use the `--no-schema` flag.
@@ -299,17 +293,14 @@ Add the resource to your browser scope in lib/hello_web/router.ex:
     resources "/posts", PostController
 ```
 
-Important: If we don't do this, our application won't compile, and we'll get an error.
+Important: If we don't do this, we'll get the following warning in our logs and the application will error when attempting to load the page:
 
 ```console
 $ mix phx.server
 Compiling 15 files (.ex)
 
-== Compilation error in file lib/hello_web/views/post_view.ex ==
-** (CompileError) lib/hello_web/templates/post/edit.html.eex:3: undefined function post_path/3
-    (stdlib) lists.erl:1338: :lists.foreach/2
-    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
-    (elixir) lib/kernel/parallel_compiler.ex:121: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
+warning: function HelloWeb.Router.Helpers.post_path/3 is undefined or private
+  lib/hello_web/templates/post/edit.html.eex:3
 ```
 
 #### [mix phx.gen.json](`Mix.Tasks.Phx.Gen.Json.run/1`)
@@ -346,17 +337,14 @@ Remember to update your repository by running migrations:
     $ mix ecto.migrate
 ```
 
-Important: If we don't do this, our application won't compile, and we'll get an error.
+Important: If we don't do this, we'll get the following warning in our logs and the application will error when attempting to load the page:
 
 ```console
 $ mix phx.server
 Compiling 19 files (.ex)
 
-== Compilation error in file lib/hello_web/controllers/post_controller.ex ==
-** (CompileError) lib/hello_web/controllers/post_controller.ex:18: undefined function post_path/3
-    (stdlib) lists.erl:1338: :lists.foreach/2
-    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
-    (elixir) lib/kernel/parallel_compiler.ex:121: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
+warning: function HelloWeb.Router.Helpers.post_path/3 is undefined or private
+  lib/hello_web/controllers/post_controller.ex:18
 ```
 
 If we don't want to create a context or schema for our resource we can use the `--no-context` flag. Note that this still requires a context module name as a parameter.
diff --git a/guides/routing.md b/guides/routing.md
index 85603abf2..eb4195963 100644
--- a/guides/routing.md
+++ b/guides/routing.md
@@ -250,10 +250,10 @@ iex> HelloWeb.Router.Helpers.page_path(HelloWeb.Endpoint, :index)
 This is significant because we can use the `page_path` function in a template to link to the root of our application. We can then use this helper in our templates:
 
 ```html
-<a href="<%= page_path(@conn, :index) %>">To the Welcome Page!</a>
+<a href="<%= Routes.page_path(@conn, :index) %>">To the Welcome Page!</a>
 ```
 
-The `page_path` function is imported into our template with `use HelloWeb, :view`. Please see the [View Guide](views.html) for more information.
+The reason we can use `Routes.page_path` instead of the full `HelloWeb.Router.Helpers.page_path` name is because `HelloWeb.Router.Helpers` is aliased as `Routes` by default in the `view/0` definition (`lib/hello_web.ex`) and made available to our templates through `use HelloWeb, :view`. We can, of course, use `HelloWeb.Router.Helpers.page_path(@conn, :index)` instead, but the convention is to use the aliased version for conciseness (note that the alias is only set automatically for use in views, controllers and templates - outside these you need either the full name, or to alias it yourself inside the module definition: `alias HelloWeb.Router.Helpers, as: Routes`). Please see the [View Guide](views.html) for more information.
 
 This pays off tremendously if we should ever have to change the path of our route in the router. Since the path helpers are built dynamically from the routes, any calls to `page_path` in our templates will still work.
 
@@ -262,41 +262,41 @@ This pays off tremendously if we should ever have to change the path of our rout
 When we ran the `phx.routes` task for our user resource, it listed the `user_path` as the path helper function for each line of output. Here is what that translates to for each action:
 
 ```elixir
-iex> import HelloWeb.Router.Helpers
+iex> alias HelloWeb.Router.Helpers, as: Routes
 iex> alias HelloWeb.Endpoint
-iex> user_path(Endpoint, :index)
+iex> Routes.user_path(Endpoint, :index)
 "/users"
 
-iex> user_path(Endpoint, :show, 17)
+iex> Routes.user_path(Endpoint, :show, 17)
 "/users/17"
 
-iex> user_path(Endpoint, :new)
+iex> Routes.user_path(Endpoint, :new)
 "/users/new"
 
-iex> user_path(Endpoint, :create)
+iex> Routes.user_path(Endpoint, :create)
 "/users"
 
-iex> user_path(Endpoint, :edit, 37)
+iex> Routes.user_path(Endpoint, :edit, 37)
 "/users/37/edit"
 
-iex> user_path(Endpoint, :update, 37)
+iex> Routes.user_path(Endpoint, :update, 37)
 "/users/37"
 
-iex> user_path(Endpoint, :delete, 17)
+iex> Routes.user_path(Endpoint, :delete, 17)
 "/users/17"
 ```
 
 What about paths with query strings? By adding an optional fourth argument of key value pairs, the path helpers will return those pairs in the query string.
 
 ```elixir
-iex> user_path(Endpoint, :show, 17, admin: true, active: false)
+iex> Routes.user_path(Endpoint, :show, 17, admin: true, active: false)
 "/users/17?admin=true&active=false"
 ```
 
 What if we need a full url instead of a path? Just replace `_path` with `_url`:
 
 ```elixir
-iex(3)> user_url(Endpoint, :index)
+iex(3)> Routes.user_url(Endpoint, :index)
 "http://localhost:4000/users"
 ```
 
@@ -344,6 +344,15 @@ iex> HelloWeb.Router.Helpers.user_post_path(Endpoint, :index, 42, active: true)
 "/users/42/posts?active=true"
 ```
 
+If we had aliased the `Helpers` module as before (it is only automatically aliased for views, templates and controllers, in this case, since we're inside `iex` we need to do it ourselves), we could instead do:
+
+```elixir
+iex> alias HelloWeb.Router.Helpers, as: Routes
+iex> alias HelloWeb.Endpoint
+iex> Routes.user_post_path(Endpoint, :index, 42, active: true)
+"/users/42/posts?active=true"
+```
+
 ## Scoped Routes
 
 Scopes are a way to group routes under a common path prefix and scoped set of plug middleware. We might want to do this for admin functionality, APIs, and especially for versioned APIs. Let's say we have user generated reviews on a site, and that those reviews first need to be approved by an admin. The semantics of these resources are quite different, and they might not share the same controller. Scopes enable us to segregate these routes.
diff --git a/guides/views.md b/guides/views.md
index 8961a3804..cf2661602 100644
--- a/guides/views.md
+++ b/guides/views.md
@@ -67,7 +67,8 @@ rendering with assigns [:conn, :view_module, :view_template]
 
 Pretty neat, right? At compile-time, Phoenix precompiles all `*.html.eex` templates and turns them into `render/2` function clauses on their respective view modules. At runtime, all templates are already loaded in memory. There's no disk reads, complex file caching, or template engine computation involved. This is also why we were able to define functions like `title/0` in our `LayoutView` and they were immediately available inside the layout's `app.html.eex` – the call to `title/0` was just a local function call!
 
-When we `use HelloWeb, :view`, we get other conveniences as well. Since the `view/0` function imports `HelloWeb.Router.Helpers`, we don't have to fully qualify path helpers in templates. Let's see how that works by changing the template for our Welcome to Phoenix page.
+When we `use HelloWeb, :view`, we get other conveniences as well. Since `view/0` aliases `HelloWeb.Router.Helpers` as `Routes` (look in `lib/hello_web.ex`), we can simply call these helpers by using `Routes.*_path` in templates. Let's see how that works by changing the template for our Welcome to Phoenix page.
+
 
 Let's open up the `lib/hello_web/templates/page/index.html.eex` and locate this stanza.
 
@@ -84,7 +85,7 @@ Then let's add a line with a link back to the same page. (The objective is to se
 <div class="jumbotron">
   <h2><%= gettext("Welcome to %{name}!", name: "Phoenix") %></h2>
   <p class="lead">A productive web framework that<br>does not compromise speed and maintainability.</p>
-  <p><a href="<%= page_path(@conn, :index) %>">Link back to this page</a></p>
+  <p><a href="<%= Routes.page_path(@conn, :index) %>">Link back to this page</a></p>
 </div>
 ```
 
@@ -94,7 +95,9 @@ Now we can reload the page and view source to see what we have.
 <a href="/">Link back to this page</a>
 ```
 
-Great, `page_path/2` evaluated to `/` as we would expect, and we didn't need to qualify it with `Phoenix.View`.
+Great, `Routes.page_path/2` evaluated to `/` as we would expect, we just had to use the alias set in `Phoenix.View`.
+
+If you happen to need access to the path helpers outside views, controllers or templates, you can either call them by the full qualified name, e.g. `HelloWeb.Router.Helpers.page_path(@conn, :index)` or alias it yourself in the calling module, by defining `alias HelloWeb.Router.Helpers, as: Routes` in the module you want to use, and then calling, e.g., `Routes.page_path(@conn, :index)`.
 
 ### More About Views
 
diff --git a/installer/README.md b/installer/README.md
index 48c596f1d..b5f6996d7 100644
--- a/installer/README.md
+++ b/installer/README.md
@@ -1,6 +1,11 @@
 ## mix phx.new
 
-Provides `phx.new` installer as an archive. To build and install it locally:
+Provides `phx.new` installer as an archive. To build and install it locally
+ensure any previous archive versions are removed:
+
+    $ mix archive.uninstall phx_new
+
+Then run:
 
     $ cd installer
     $ MIX_ENV=prod mix do archive.build, archive.install
diff --git a/installer/templates/phx_assets/phoenix.css b/installer/templates/phx_assets/phoenix.css
index a0165bb62..015800067 100644
--- a/installer/templates/phx_assets/phoenix.css
+++ b/installer/templates/phx_assets/phoenix.css
@@ -9,12 +9,19 @@
 *,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#000000;font-family:'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#0069d9;border:0.1rem solid #0069d9;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#0069d9;border-color:#0069d9}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#0069d9}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#0069d9}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#0069d9}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#0069d9}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #0069d9;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,textarea:focus,select:focus{border-color:#0069d9;outline:0}select{background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="#d1d1d1" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="#0069d9" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#0069d9;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}
 
 /* General style */
+h1{font-size: 3.6rem; line-height: 1.25}
+h2{font-size: 2.8rem; line-height: 1.3}
+h3{font-size: 2.2rem; letter-spacing: -.08rem; line-height: 1.35}
+h4{font-size: 1.8rem; letter-spacing: -.05rem; line-height: 1.5}
+h5{font-size: 1.6rem; letter-spacing: 0; line-height: 1.4}
+h6{font-size: 1.4rem; letter-spacing: 0; line-height: 1.2}
+
 .container{
-  margin:0 auto;
-  max-width:80.0rem;
-  padding:0 2.0rem;
-  position:relative;
-  width:100%
+  margin: 0 auto;
+  max-width: 80.0rem;
+  padding: 0 2.0rem;
+  position: relative;
+  width: 100%
 }
 select {
   width: auto;
diff --git a/installer/templates/phx_assets/webpack/webpack.config.js b/installer/templates/phx_assets/webpack/webpack.config.js
index 5225785b7..45ab14fdd 100644
--- a/installer/templates/phx_assets/webpack/webpack.config.js
+++ b/installer/templates/phx_assets/webpack/webpack.config.js
@@ -1,4 +1,5 @@
 const path = require('path');
+const glob = require('glob');
 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
 const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
@@ -11,7 +12,9 @@ module.exports = (env, options) => ({
       new OptimizeCSSAssetsPlugin({})
     ]
   },
-  entry: './js/app.js',
+  entry: {
+      './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js'))
+  },
   output: {
     filename: 'app.js',
     path: path.resolve(__dirname, '../priv/static/js')
diff --git a/installer/templates/phx_single/config/dev.exs b/installer/templates/phx_single/config/dev.exs
index fb25c07c2..147f04828 100644
--- a/installer/templates/phx_single/config/dev.exs
+++ b/installer/templates/phx_single/config/dev.exs
@@ -29,6 +29,7 @@ config :<%= app_name %>, <%= endpoint_module %>,
 #
 #     https: [
 #       port: 4001,
+#       cipher_suite: :strong,
 #       keyfile: "priv/cert/selfsigned_key.pem",
 #       certfile: "priv/cert/selfsigned.pem"
 #     ],
diff --git a/installer/templates/phx_single/config/prod.exs b/installer/templates/phx_single/config/prod.exs
index 75f628e4e..375acea39 100644
--- a/installer/templates/phx_single/config/prod.exs
+++ b/installer/templates/phx_single/config/prod.exs
@@ -25,11 +25,13 @@ config :logger, level: :info
 #     config :<%= app_name %>, <%= endpoint_module %>,
 #       ...
 #       url: [host: "example.com", port: 443],
-#       https: [:inet6,
-#               port: 443,
-#               cipher_suite: :strong,
-#               keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
-#               certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
+#       https: [
+#         :inet6,
+#         port: 443,
+#         cipher_suite: :strong,
+#         keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
+#         certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
+#       ]
 #
 # The `cipher_suite` is set to `:strong` to support only the
 # latest and more secure SSL ciphers. This means old browsers
diff --git a/installer/templates/phx_single/mix.exs b/installer/templates/phx_single/mix.exs
index f6efa74d7..4e3d5f17c 100644
--- a/installer/templates/phx_single/mix.exs
+++ b/installer/templates/phx_single/mix.exs
@@ -59,7 +59,7 @@ defmodule <%= app_module %>.MixProject do
     [
       "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
       "ecto.reset": ["ecto.drop", "ecto.setup"],
-      "test": ["ecto.create --quiet", "ecto.migrate", "test"]
+      test: ["ecto.create --quiet", "ecto.migrate", "test"]
     ]
   end<% end %>
 end
diff --git a/installer/templates/phx_umbrella/apps/app_name/mix.exs b/installer/templates/phx_umbrella/apps/app_name/mix.exs
index af171fa34..472f4b23b 100644
--- a/installer/templates/phx_umbrella/apps/app_name/mix.exs
+++ b/installer/templates/phx_umbrella/apps/app_name/mix.exs
@@ -51,7 +51,7 @@ defmodule <%= app_module %>.MixProject do
     [<%= if ecto do %>
       "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
       "ecto.reset": ["ecto.drop", "ecto.setup"],
-      "test": ["ecto.create --quiet", "ecto.migrate", "test"]
+      test: ["ecto.create --quiet", "ecto.migrate", "test"]
     <% end %>]
   end
 end
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs b/installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs
index 63f46513b..d9b79a20b 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs
+++ b/installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs
@@ -33,6 +33,7 @@ config :<%= web_app_name %>, <%= endpoint_module %>,
 #
 #     https: [
 #       port: 4001,
+#       cipher_suite: :strong,
 #       keyfile: "priv/cert/selfsigned_key.pem",
 #       certfile: "priv/cert/selfsigned.pem"
 #     ],
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/config/prod.exs b/installer/templates/phx_umbrella/apps/app_name_web/config/prod.exs
index 880e6a6a4..2c32b6807 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/config/prod.exs
+++ b/installer/templates/phx_umbrella/apps/app_name_web/config/prod.exs
@@ -26,11 +26,13 @@ config :<%= web_app_name %>, <%= endpoint_module %>,
 #     config :<%= app_name %>, <%= endpoint_module %>,
 #       ...
 #       url: [host: "example.com", port: 443],
-#       https: [:inet6,
-#               port: 443,
-#               cipher_suite: :strong,
-#               keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
-#               certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
+#       https: [
+#         :inet6,
+#         port: 443,
+#         cipher_suite: :strong,
+#         keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
+#         certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
+#       ]
 #
 # The `cipher_suite` is set to `:strong` to support only the
 # latest and more secure SSL ciphers. This means old browsers
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/mix.exs b/installer/templates/phx_umbrella/apps/app_name_web/mix.exs
index 05e8338ed..9165f1c2b 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/mix.exs
+++ b/installer/templates/phx_umbrella/apps/app_name_web/mix.exs
@@ -54,6 +54,6 @@ defmodule <%= web_namespace %>.MixProject do
   #
   # See the documentation for `Mix` for more info on aliases.
   defp aliases do
-    [<%= if ecto do %>"test": ["ecto.create --quiet", "ecto.migrate", "test"]<% end %>]
+    [<%= if ecto do %>test: ["ecto.create --quiet", "ecto.migrate", "test"]<% end %>]
   end
 end
diff --git a/installer/templates/phx_web/templates/layout/app.html.eex b/installer/templates/phx_web/templates/layout/app.html.eex
index b28aabe0b..91a012025 100644
--- a/installer/templates/phx_web/templates/layout/app.html.eex
+++ b/installer/templates/phx_web/templates/layout/app.html.eex
@@ -5,7 +5,7 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
     <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, shrink-to-fit=no, user-scalable=no"/>
     <title><%= app_module %> · Phoenix Framework</title>
-    <link rel="stylesheet" href="<%%= Routes.static_path(@conn, "/css/app.css") %>"/>
+    <link rel="stylesheet" href="<%%= Routes.static_url(@conn, "/css/app.css") %>"/>
   </head>
   <body>
     <header>
@@ -16,7 +16,7 @@
           </ul>
         </nav>
         <a href="http://phoenixframework.org/" class="phx-logo">
-          <img src="<%%= Routes.static_path(@conn, "/images/phoenix.png") %>" alt="Phoenix Framework Logo"/>
+          <img src="<%%= Routes.static_url(@conn, "/images/phoenix.png") %>" alt="Phoenix Framework Logo"/>
         </a>
       </section>
     </header>
@@ -25,6 +25,6 @@
       <p class="alert alert-danger" role="alert"><%%= get_flash(@conn, :error) %></p>
       <%%= render @view_module, @view_template, assigns %>
     </main>
-    <script type="text/javascript" src="<%%= Routes.static_path(@conn, "/js/app.js") %>"></script>
+    <script type="text/javascript" src="<%%= Routes.static_url(@conn, "/js/app.js") %>"></script>
   </body>
 </html>
diff --git a/installer/templates/phx_web/templates/page/index.html.eex b/installer/templates/phx_web/templates/page/index.html.eex
index 3c79b5a16..a96447a4d 100644
--- a/installer/templates/phx_web/templates/page/index.html.eex
+++ b/installer/templates/phx_web/templates/page/index.html.eex
@@ -1,11 +1,11 @@
 <section class="phx-hero">
-  <h2><%%= gettext "Welcome to %{name}!", name: "Phoenix" %></h2>
+  <h1><%%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
   <p>A productive web framework that<br/>does not compromise speed and maintainability.</p>
 </section>
 
 <section class="row">
   <article class="column">
-    <h4>Resources</h4>
+    <h2>Resources</h2>
     <ul>
       <li>
         <a href="https://hexdocs.pm/phoenix/overview.html">Guides &amp; Docs</a>
@@ -19,7 +19,7 @@
     </ul>
   </article>
   <article class="column">
-    <h4>Help</h4>
+    <h2>Help</h2>
     <ul>
       <li>
         <a href="https://elixirforum.com/c/phoenix-forum">Forum</a>
diff --git a/lib/mix/phoenix/context.ex b/lib/mix/phoenix/context.ex
index d0bae4c37..e9fa61d3e 100644
--- a/lib/mix/phoenix/context.ex
+++ b/lib/mix/phoenix/context.ex
@@ -25,7 +25,7 @@ defmodule Mix.Phoenix.Context do
     ctx_app   = opts[:context_app] || Mix.Phoenix.context_app()
     base      = Module.concat([Mix.Phoenix.context_base(ctx_app)])
     module    = Module.concat(base, context_name)
-    alias     = module |> Module.split() |> tl() |> Module.concat()
+    alias     = Module.concat([module |> Module.split() |> List.last()])
     basedir   = Phoenix.Naming.underscore(context_name)
     basename  = Path.basename(basedir)
     dir       = Mix.Phoenix.context_lib_path(ctx_app, basedir)
diff --git a/lib/mix/tasks/phx.gen.cert.ex b/lib/mix/tasks/phx.gen.cert.ex
index 0693fb3d2..fc0cca8ee 100644
--- a/lib/mix/tasks/phx.gen.cert.ex
+++ b/lib/mix/tasks/phx.gen.cert.ex
@@ -18,8 +18,8 @@ defmodule Mix.Tasks.Phx.Gen.Cert do
   @moduledoc """
   Generates a self-signed certificate for HTTPS testing.
 
-    mix phx.gen.cert
-    mix phx.gen.cert my-app my-app.local my-app.internal.example.com
+      mix phx.gen.cert
+      mix phx.gen.cert my-app my-app.local my-app.internal.example.com
 
   Creates a private key and a self-signed certificate in PEM format. These
   files can be referenced in the `certfile` and `keyfile` parameters of an
@@ -91,7 +91,8 @@ defmodule Mix.Tasks.Phx.Gen.Cert do
 
         {:error, :not_supported} ->
           Mix.raise("""
-          Failed to generate an RSA key pair
+          Failed to generate an RSA key pair.
+
           This Mix task requires Erlang/OTP 20 or later. Please upgrade to a
           newer version, or use another tool, such as OpenSSL, to generate a
           certificate.
@@ -115,11 +116,15 @@ defmodule Mix.Tasks.Phx.Gen.Cert do
     Mix.shell().info("""
 
     If you have not already done so, please update your HTTPS Endpoint
-    configuration in #{Mix.Phoenix.web_path(app)}/config/dev.exs, e.g.:
+    configuration in #{Mix.Phoenix.web_path(app)}/config/dev.exs:
 
       config #{inspect(app)}, #{base}Web.Endpoint,
         http: [port: 4000],
-        https: [port: 4001, certfile: "#{certfile}", keyfile: "#{keyfile}"],
+        https: [
+          port: 4001,
+          certfile: "#{certfile}",
+          keyfile: "#{keyfile}"
+        ],
         ...
 
     #{@warning}
diff --git a/lib/phoenix/channel/server.ex b/lib/phoenix/channel/server.ex
index 6fe5bf8ac..bc73b6a5f 100644
--- a/lib/phoenix/channel/server.ex
+++ b/lib/phoenix/channel/server.ex
@@ -41,8 +41,9 @@ defmodule Phoenix.Channel.Server do
     Phoenix.Endpoint.instrument socket, :phoenix_channel_join, instrument, fn ->
       ref = make_ref()
       key = {self(), ref}
+      args = [socket, payload, self(), ref]
 
-      case PoolSupervisor.start_child(socket.handler, key, [socket, payload, self(), ref]) do
+      case PoolSupervisor.start_child(socket.endpoint, socket.handler, key, args) do
         {:ok, :undefined} ->
           log_join socket, topic, fn -> "Replied #{topic} :error" end
           receive do: ({^ref, reply} -> {:error, reply})
diff --git a/lib/phoenix/config.ex b/lib/phoenix/config.ex
index 301995384..e7df17b90 100644
--- a/lib/phoenix/config.ex
+++ b/lib/phoenix/config.ex
@@ -13,22 +13,18 @@ defmodule Phoenix.Config do
   Starts a Phoenix configuration handler.
   """
   def start_link(module, config, defaults, opts \\ []) do
-    GenServer.start_link(__MODULE__, {module, config, defaults}, opts)
+    permanent = Keyword.keys(defaults)
+    GenServer.start_link(__MODULE__, {module, config, permanent}, opts)
   end
 
   @doc """
-  Stops Phoenix configuration handler for the module.
-  """
-  def stop(module) do
-    [__config__: pid] = :ets.lookup(module, :__config__)
-    GenServer.call(pid, :stop)
-  end
+  Adds permanent configuration.
 
-  @doc """
-  Deep merges the given configuration.
+  Permanent configuration is not deleted on hot code reload.
   """
-  def merge(config1, config2) do
-    Keyword.merge(config1, config2, &merger/3)
+  def permanent(module, key, value) do
+    pid = :ets.lookup_element(module, :__config__, 2)
+    GenServer.call(pid, {:permanent, key, value})
   end
 
   @doc """
@@ -44,12 +40,15 @@ defmodule Phoenix.Config do
   @spec cache(module, term, (module -> {:cache | :nocache, term})) :: term
   def cache(module, key, fun) do
     case :ets.lookup(module, key) do
-      [{^key, :cache, val}] -> val
+      [{^key, :cache, val}] ->
+        val
+
       [] ->
         case fun.(module) do
           {:cache, val} ->
             :ets.insert(module, {key, :cache, val})
             val
+
           {:nocache, val} ->
             val
         end
@@ -71,18 +70,31 @@ defmodule Phoenix.Config do
   Useful to read a particular value at compilation time.
   """
   def from_env(otp_app, module, defaults) do
-    merge(defaults, fetch_config(otp_app, module))
+    Keyword.merge(defaults, fetch_config(otp_app, module), &merger/3)
   end
+
   defp fetch_config(otp_app, module) do
     case Application.fetch_env(otp_app, module) do
-      {:ok, conf} -> conf
+      {:ok, conf} ->
+        conf
+
       :error ->
-        Logger.warn "no configuration found for otp_app " <>
-                    "#{inspect otp_app} and module #{inspect module}"
+        Logger.warn(
+          "no configuration found for otp_app #{inspect(otp_app)} and module #{inspect(module)}"
+        )
+
         []
     end
   end
 
+  defp merger(_k, v1, v2) do
+    if Keyword.keyword?(v1) and Keyword.keyword?(v2) do
+      Keyword.merge(v1, v2, &merger/3)
+    else
+      v2
+    end
+  end
+
   @doc """
   Changes the configuration for the given module.
 
@@ -98,52 +110,37 @@ defmodule Phoenix.Config do
 
   # Callbacks
 
-  def init({module, config, defaults}) do
+  def init({module, config, permanent}) do
     :ets.new(module, [:named_table, :public, read_concurrency: true])
-    :ets.insert(module, [__config__: self()])
-    update(module, config)
-    {:ok, {module, defaults}}
+    update(module, config, [])
+    :ets.insert(module, __config__: self())
+    {:ok, {module, [:__config__ | permanent]}}
   end
 
-  def handle_call({:config_change, changed, removed}, _from, {module, defaults}) do
-    cond do
-      changed = changed[module] ->
-        update(module, merge(defaults, changed))
-        {:reply, :ok, {module, defaults}}
-      module in removed ->
-        stop(module, defaults)
-      true ->
-        {:reply, :ok, {module, defaults}}
-    end
+  def handle_call({:permanent, key, value}, _from, {module, permanent}) do
+    :ets.insert(module, {key, value})
+    {:reply, :ok, {module, [key | permanent]}}
   end
 
-  def handle_call(:stop, _from, {module, defaults}) do
-    stop(module, defaults)
-  end
+  def handle_call({:config_change, changed, removed}, _from, {module, permanent}) do
+    cond do
+      changed = changed[module] ->
+        update(module, changed, permanent)
+        {:reply, :ok, {module, permanent}}
 
-  # Helpers
+      module in removed ->
+        {:stop, :normal, :ok, {module, permanent}}
 
-  defp merger(_k, v1, v2) do
-    if Keyword.keyword?(v1) and Keyword.keyword?(v2) do
-      Keyword.merge(v1, v2, &merger/3)
-    else
-      v2
+      true ->
+        {:reply, :ok, {module, permanent}}
     end
   end
 
-  defp update(module, config) do
-    old_keys = keys(:ets.tab2list(module))
-    new_keys = [:__config__|keys(config)]
-    Enum.each old_keys -- new_keys, &:ets.delete(module, &1)
+  defp update(module, config, permanent) do
+    old_keys = :ets.select(module, [{{:"$1", :_}, [], [:"$1"]}])
+    new_keys = Enum.map(config, &elem(&1, 0))
+    Enum.each((old_keys -- new_keys) -- permanent, &:ets.delete(module, &1))
     :ets.insert(module, config)
-  end
-
-  defp keys(data) do
-    Enum.map(data, &elem(&1, 0))
-  end
-
-  defp stop(module, defaults) do
-    :ets.delete(module)
-    {:stop, :normal, :ok, {module, defaults}}
+    clear_cache(module)
   end
 end
diff --git a/lib/phoenix/endpoint/cowboy2_adapter.ex b/lib/phoenix/endpoint/cowboy2_adapter.ex
index 53c95475c..9d67915ba 100644
--- a/lib/phoenix/endpoint/cowboy2_adapter.ex
+++ b/lib/phoenix/endpoint/cowboy2_adapter.ex
@@ -68,6 +68,7 @@ defmodule Phoenix.Endpoint.Cowboy2Adapter do
     server = "cowboy #{Application.spec(:cowboy)[:vsn]}"
     "Running #{inspect endpoint} with #{server} at #{uri(scheme, ref)}"
   end
+
   defp uri(scheme, ref) do
     case :ranch.get_addr(ref) do
       {:local, unix_path} ->
diff --git a/lib/phoenix/endpoint/supervisor.ex b/lib/phoenix/endpoint/supervisor.ex
index 09722518b..fe5b1ba76 100644
--- a/lib/phoenix/endpoint/supervisor.ex
+++ b/lib/phoenix/endpoint/supervisor.ex
@@ -37,9 +37,9 @@ defmodule Phoenix.Endpoint.Supervisor do
     end
 
     children =
+      config_children(mod, conf, otp_app) ++
       pubsub_children(mod, conf) ++
       socket_children(mod) ++
-      config_children(mod, conf, otp_app) ++
       server_children(mod, conf, otp_app, server?) ++
       watcher_children(mod, conf, server?)
 
@@ -58,10 +58,10 @@ defmodule Phoenix.Endpoint.Supervisor do
     end
   end
 
-  defp socket_children(mod) do
-    mod.__sockets__
+  defp socket_children(endpoint) do
+    endpoint.__sockets__
     |> Enum.uniq_by(&elem(&1, 1))
-    |> Enum.map(fn {_, mod, opts} -> mod.child_spec(opts) end)
+    |> Enum.map(fn {_, socket, opts} -> socket.child_spec([endpoint: endpoint] ++ opts) end)
   end
 
   defp config_children(mod, conf, otp_app) do
diff --git a/lib/phoenix/router.ex b/lib/phoenix/router.ex
index ddbb16479..de362aa80 100644
--- a/lib/phoenix/router.ex
+++ b/lib/phoenix/router.ex
@@ -101,7 +101,7 @@ defmodule Phoenix.Router do
   The route above will dispatch to `MyAppWeb.PageController`. This syntax
   is not only convenient for developers, since we don't have to repeat
   the `MyAppWeb.` prefix on all routes, but it also allows Phoenix to put
-  less pressure in the Elixir compiler. If instead we had written:
+  less pressure on the Elixir compiler. If instead we had written:
 
       get "/pages/:id", MyAppWeb.PageController, :show
 
diff --git a/lib/phoenix/socket.ex b/lib/phoenix/socket.ex
index c0ac4fc2d..8a42638e7 100644
--- a/lib/phoenix/socket.ex
+++ b/lib/phoenix/socket.ex
@@ -430,11 +430,14 @@ defmodule Phoenix.Socket do
 
   def __child_spec__(handler, opts) do
     import Supervisor.Spec
+    endpoint = Keyword.fetch!(opts, :endpoint)
+    shutdown = Keyword.get(opts, :shutdown, 5_000)
     partitions = Keyword.get(opts, :partitions) || System.schedulers_online()
 
-    worker_opts = [shutdown: Keyword.get(opts, :shutdown, 5_000), restart: :temporary]
+    worker_opts = [shutdown: shutdown, restart: :temporary]
     worker = worker(Phoenix.Channel.Server, [], worker_opts)
-    supervisor(Phoenix.Socket.PoolSupervisor, [{handler, partitions, worker}], id: handler)
+    args = {endpoint, handler, partitions, worker}
+    supervisor(Phoenix.Socket.PoolSupervisor, [args], id: handler)
   end
 
   def __connect__(handler, map) do
diff --git a/lib/phoenix/socket/pool_supervisor.ex b/lib/phoenix/socket/pool_supervisor.ex
index 8be5660df..73acfda9e 100644
--- a/lib/phoenix/socket/pool_supervisor.ex
+++ b/lib/phoenix/socket/pool_supervisor.ex
@@ -2,29 +2,40 @@ defmodule Phoenix.Socket.PoolSupervisor do
   @moduledoc false
   use Supervisor
 
-  def start_link({name, _partitions, _worker} = triplet) do
-    Supervisor.start_link(__MODULE__, triplet, name: name)
+  def start_link(args) do
+    Supervisor.start_link(__MODULE__, args)
   end
 
-  def start_child(name, key, args) do
-    partitions = :ets.lookup_element(name, :partitions, 2)
-    sup = :ets.lookup_element(name, :erlang.phash2(key, partitions), 2)
+  def start_child(endpoint, name, key, args) do
+    ets = endpoint.config({:socket, name})
+    partitions = :ets.lookup_element(ets, :partitions, 2)
+    sup = :ets.lookup_element(ets, :erlang.phash2(key, partitions), 2)
     Supervisor.start_child(sup, args)
   end
 
   @doc false
-  def init({name, partitions, worker}) do
+  def start_pooled(worker, ref, i) do
+    case Supervisor.start_link([worker], strategy: :simple_one_for_one) do
+      {:ok, pid} ->
+        :ets.insert(ref, {i, pid})
+        {:ok, pid}
+
+      {:error, reason} ->
+        {:error, reason}
+    end
+  end
+
+  @doc false
+  def init({endpoint, name, partitions, worker}) do
     import Supervisor.Spec
 
-    ref = :ets.new(name, [:named_table, :public, read_concurrency: true])
+    ref = :ets.new(name, [:public, read_concurrency: true])
     :ets.insert(ref, {:partitions, partitions})
+    Phoenix.Config.permanent(endpoint, {:socket, name}, ref)
 
     children =
       for i <- 0..(partitions - 1) do
-        name = :"#{name}#{i}"
-        :ets.insert(ref, {i, name})
-        supervisor_opts = [strategy: :simple_one_for_one, name: name]
-        supervisor(Supervisor, [[worker], supervisor_opts], id: name)
+        supervisor(__MODULE__, [worker, ref, i], id: i, function: :start_pooled)
       end
 
     supervise(children, strategy: :one_for_one)
diff --git a/lib/phoenix/socket/transport.ex b/lib/phoenix/socket/transport.ex
index 9ebe72ad8..66609db1e 100644
--- a/lib/phoenix/socket/transport.ex
+++ b/lib/phoenix/socket/transport.ex
@@ -363,6 +363,8 @@ defmodule Phoenix.Socket.Transport do
         Logger.error """
         Could not check origin for Phoenix.Socket transport.
 
+        Origin of the request: #{origin}
+
         This happens when you are attempting a socket connection to
         a different host than the one configured in your config/
         files. For example, in development the host is configured
diff --git a/lib/phoenix/view.ex b/lib/phoenix/view.ex
index 8d181a493..c947ac923 100644
--- a/lib/phoenix/view.ex
+++ b/lib/phoenix/view.ex
@@ -10,18 +10,26 @@ defmodule Phoenix.View do
 
   ## Examples
 
-  Phoenix defines the view template at `lib/web/web.ex`:
+  Phoenix defines the view template at `lib/your_app_web.ex`:
 
       defmodule YourAppWeb do
+        # ...
+
         def view do
           quote do
-            use Phoenix.View, root: "lib/web/templates"
+            use Phoenix.View, root: "lib/your_app_web/templates", namespace: "web"
+            
+            # Import convenience functions from controllers
+            import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1]
 
-            # Import common functionality
-            import YourApp.Router.Helpers
-
-            # Use Phoenix.HTML to import all HTML functions (forms, tags, etc)
+            # Use all HTML functionality (forms, tags, etc)
             use Phoenix.HTML
+          
+            import YourAppWeb.ErrorHelpers
+            import YourAppWeb.Gettext
+
+            # Alias the Helpers module as Routes
+            alias  YourAppWeb.Router.Helpers, as: Routes
           end
         end
 
@@ -34,11 +42,11 @@ defmodule Phoenix.View do
         use YourAppWeb, :view
       end
 
-  Because we have defined the template root to be "lib/web/templates", `Phoenix.View`
-  will automatically load all templates at "web/templates/user" and include them
+  Because we have defined the template root to be "lib/your_app_web/templates", `Phoenix.View`
+  will automatically load all templates at "your_app_web/templates/user" and include them
   in the `YourApp.UserView`. For example, imagine we have the template:
 
-      # web/templates/user/index.html.eex
+      # your_app_web/templates/user/index.html.eex
       Hello <%= @name %>
 
   The `.eex` extension maps to a template engine which tells Phoenix how
@@ -276,7 +284,7 @@ defmodule Phoenix.View do
 
   To use a precompiled template, create a `scripts.html.eex` file in the `templates`
   directory for the corresponding view you want it to render for. For example,
-  for the `UserView`, create the `scripts.html.eex` file at `web/templates/user/`.
+  for the `UserView`, create the `scripts.html.eex` file at `your_app_web/templates/user/`.
 
   ## Rendering based on controller template
 
diff --git a/priv/templates/phx.gen.html/edit.html.eex b/priv/templates/phx.gen.html/edit.html.eex
index f76e054f6..a846d7274 100644
--- a/priv/templates/phx.gen.html/edit.html.eex
+++ b/priv/templates/phx.gen.html/edit.html.eex
@@ -1,4 +1,4 @@
-<h2>Edit <%= schema.human_singular %></h2>
+<h1>Edit <%= schema.human_singular %></h1>
 
 <%%= render "form.html", Map.put(assigns, :action, Routes.<%= schema.route_helper %>_path(@conn, :update, @<%= schema.singular %>)) %>
 
diff --git a/priv/templates/phx.gen.html/index.html.eex b/priv/templates/phx.gen.html/index.html.eex
index 20512bb2c..9541f23e4 100644
--- a/priv/templates/phx.gen.html/index.html.eex
+++ b/priv/templates/phx.gen.html/index.html.eex
@@ -1,4 +1,4 @@
-<h2>Listing <%= schema.human_plural %></h2>
+<h1>Listing <%= schema.human_plural %></h1>
 
 <table>
   <thead>
diff --git a/priv/templates/phx.gen.html/new.html.eex b/priv/templates/phx.gen.html/new.html.eex
index 402eef1db..135bafae9 100644
--- a/priv/templates/phx.gen.html/new.html.eex
+++ b/priv/templates/phx.gen.html/new.html.eex
@@ -1,4 +1,4 @@
-<h2>New <%= schema.human_singular %></h2>
+<h1>New <%= schema.human_singular %></h1>
 
 <%%= render "form.html", Map.put(assigns, :action, Routes.<%= schema.route_helper %>_path(@conn, :create)) %>
 
diff --git a/priv/templates/phx.gen.html/show.html.eex b/priv/templates/phx.gen.html/show.html.eex
index 1fcd007ba..c19a2c955 100644
--- a/priv/templates/phx.gen.html/show.html.eex
+++ b/priv/templates/phx.gen.html/show.html.eex
@@ -1,4 +1,4 @@
-<h2>Show <%= schema.human_singular %></h2>
+<h1>Show <%= schema.human_singular %></h1>
 
 <ul>
 <%= for {k, _} <- schema.attrs do %>
diff --git a/priv/templates/phx.gen.json/fallback_controller.ex b/priv/templates/phx.gen.json/fallback_controller.ex
index 177a53756..a4b88bf7a 100644
--- a/priv/templates/phx.gen.json/fallback_controller.ex
+++ b/priv/templates/phx.gen.json/fallback_controller.ex
@@ -6,14 +6,14 @@ defmodule <%= inspect context.web_module %>.FallbackController do
   """
   use <%= inspect context.web_module %>, :controller
 
-  def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
+  <%= if schema.generate? do %>def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
     conn
     |> put_status(:unprocessable_entity)
     |> put_view(<%= inspect context.web_module %>.ChangesetView)
     |> render("error.json", changeset: changeset)
   end
 
-  def call(conn, {:error, :not_found}) do
+  <% end %>def call(conn, {:error, :not_found}) do
     conn
     |> put_status(:not_found)
     |> put_view(<%= inspect context.web_module %>.ErrorView)


Test patch:
diff --git a/guides/testing/testing_controllers.md b/guides/testing/testing_controllers.md
index 405732959..0f5e5ec23 100644
--- a/guides/testing/testing_controllers.md
+++ b/guides/testing/testing_controllers.md
@@ -136,7 +136,7 @@ defmodule HelloWeb.UserControllerTest do
 
     response =
       conn
-      |> get(user_path(conn, :index))
+      |> get(Routes.user_path(conn, :index))
       |> json_response(200)
 
     expected = %{
@@ -150,7 +150,7 @@ defmodule HelloWeb.UserControllerTest do
   end
 ```
 
-Let's take a look at what's going on here. First we alias `Hello.Accounts`, the context module that provides us with our repository manipulation functions. When we use the `HelloWeb.ConnCase` module, it sets things up such that each connection is wrapped in a transaction, *and* all of the database interactions inside of the test use the same database connection and transaction. This module also sets up a `conn` attribute in our ExUnit context, using `Phoenix.ConnCase/build_conn/0`. We then pattern match this to use it in each test case. For details, take a look at the file `test/support/conn_case.ex`, as well as the [Ecto documentation for SQL.Sandbox](https://hexdocs.pm/ecto/Ecto.Adapters.SQL.Sandbox.html). We could put a `build_conn/0` call inside of each test, but it is cleaner to use a setup block to do it.
+Let's take a look at what's going on here. First we alias `Hello.Accounts`, the context module that provides us with our repository manipulation functions. When we use the `HelloWeb.ConnCase` module, it sets things up such that each connection is wrapped in a transaction, *and* all of the database interactions inside of the test use the same database connection and transaction. This module also sets up a `conn` attribute in our ExUnit context, using `Phoenix.ConnCase.build_conn/0`. We then pattern match this to use it in each test case. For details, take a look at the file `test/support/conn_case.ex`, as well as the [Ecto documentation for SQL.Sandbox](https://hexdocs.pm/ecto/Ecto.Adapters.SQL.Sandbox.html). We could put a `build_conn/0` call inside of each test, but it is cleaner to use a setup block to do it.
 
 The index test then hooks into the context to extract the contents of the `:conn` key. We then create two users using the `Hello.Accounts.create_user/1` function. Again, note that this function accesses the test repo, but even though we don't pass the `conn` variable to the call, it still uses the same connection and puts these new users inside the same database transaction. Next the `conn` is piped to a `get` function to make a `GET` request to our `UserController` index action, which is in turn piped into `json_response/2` along with the expected HTTP status code. This will return the JSON from the response body, when everything is wired up properly. We represent the JSON we want the controller action to return with the variable `expected`, and assert that the `response` and `expected` are the same.
 
@@ -267,7 +267,7 @@ test "Responds with user info if the user is found", %{conn: conn} do
 
   response =
     conn
-    |> get(user_path(conn, :show, user.id))
+    |> get(Routes.user_path(conn, :show, user.id))
     |> json_response(200)
 
   expected = %{"data" => %{"email" => user.email, "name" => user.name}}
@@ -302,7 +302,7 @@ describe "show/2" do
 
     response =
       conn
-      |> get(user_path(conn, :show, user.id))
+      |> get(Routes.user_path(conn, :show, user.id))
       |> json_response(200)
 
     expected = %{"data" => %{"email" => user.email, "name" => user.name}}
@@ -324,7 +324,7 @@ Finally, let's change our `index/2` test to also use the new `create_user` funct
 
       response =
         conn
-        |> get(user_path(conn, :index))
+        |> get(Routes.user_path(conn, :index))
         |> json_response(200)
 
       expected = %{"data" => [%{"name" => user.name, "email" => user.email}]}
@@ -393,7 +393,7 @@ Walking through our TDD steps, we add a test that supplies a non-existent user i
 
 ```elixir
 test "Responds with a message indicating user not found", %{conn:  conn} do
-  conn = get(conn, user_path(conn, :show, -1))
+  conn = get(conn, Routes.user_path(conn, :show, -1))
 
   assert text_response(conn, 404) =~ "User not found"
 end
diff --git a/test/mix/tasks/phx.gen.context_test.exs b/test/mix/tasks/phx.gen.context_test.exs
index 17dea53ec..4fd82ecf4 100644
--- a/test/mix/tasks/phx.gen.context_test.exs
+++ b/test/mix/tasks/phx.gen.context_test.exs
@@ -47,7 +47,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do
       context = Context.new("Site.Blog", schema, [])
 
       assert %Context{
-        alias: Site.Blog,
+        alias: Blog,
         base_module: Phoenix,
         basename: "blog",
         module: Phoenix.Site.Blog,
diff --git a/test/phoenix/config_test.exs b/test/phoenix/config_test.exs
index 844aabb1c..1b12ac3cb 100644
--- a/test/phoenix/config_test.exs
+++ b/test/phoenix/config_test.exs
@@ -25,9 +25,6 @@ defmodule Phoenix.ConfigTest do
     assert :ets.lookup(meta.test, :parsers) == [parsers: false]
     assert :ets.lookup(meta.test, :static)  == [static: [at: "/"]]
     assert :ets.lookup(meta.test, :custom)  == [custom: true]
-
-    assert stop(meta.test) == :ok
-    assert :ets.info(meta.test, :name) == :undefined
   end
 
   test "can change configuration", meta do
diff --git a/test/phoenix/socket/transport_test.exs b/test/phoenix/socket/transport_test.exs
index b81a383d4..6f7e99ec6 100644
--- a/test/phoenix/socket/transport_test.exs
+++ b/test/phoenix/socket/transport_test.exs
@@ -4,6 +4,8 @@ defmodule Phoenix.Socket.TransportTest do
   use ExUnit.Case, async: true
   use RouterHelper
 
+  import ExUnit.CaptureLog
+
   alias Phoenix.Socket.Transport
 
   Application.put_env :phoenix, __MODULE__.Endpoint,
@@ -16,7 +18,7 @@ defmodule Phoenix.Socket.TransportTest do
   end
 
   setup_all do
-    Endpoint.start_link
+    Endpoint.start_link()
     :ok
   end
 
@@ -79,15 +81,21 @@ defmodule Phoenix.Socket.TransportTest do
       assert conn.halted
     end
 
-
     test "halts invalid URIs when check origin is configured" do
+      Logger.enable(self())
       origins = ["//example.com", "http://scheme.com", "//port.com:81"]
 
-      for config <- [origins, true] do
-        assert check_origin("file://", check_origin: config).halted
-        assert check_origin("null", check_origin: config).halted
-        assert check_origin("", check_origin: config).halted
-      end
+      logs =
+        capture_log(fn ->
+          for config <- [origins, true] do
+            assert check_origin("file://", check_origin: config).halted
+            assert check_origin("null", check_origin: config).halted
+            assert check_origin("", check_origin: config).halted
+          end
+        end)
+
+      assert logs =~ "Origin of the request: file://"
+      assert logs =~ "Origin of the request: null"
     end
 
     def invalid_allowed?(%URI{host: nil}), do: true