Repository: phoenixframework/phoenix (elixir)

Issue:
Generated FallbackController blindly relies on Ecto
### Environment

* Elixir version (elixir -v): Elixir 1.6.5
* Phoenix version (mix deps): phoenix 1.3.3
* Operating system: Linux

### Expected behavior
When using `--no-ecto` with `mix phx.new`, other generators shouldn't write code that depends on Ecto.

### Actual behavior
Calling `mix phx.gen.json`, even with `--no-schema`, writes the following in FallbackController:
```elixir
  def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
    conn
    |> put_status(:unprocessable_entity)
    |> render(NoectotestWeb.ChangesetView, "error.json", changeset: changeset)
  end
```
Now it doesn't compile because Ecto isn't a dep of the app.

Ecto's very useful, so most users will be using it. But I think it's reasonable for the generators to check that Ecto is available before trying to use it, especially if `--no-schema` is being used.

Pull request description:
Don't rely on Ecto.Changeset when `--no-schema` is passed.
To fix #2968.

Since FallbackController is usually not edited for a while after being generated, we shouldn't write an automatic compiler error into it.

Patch:
diff --git a/guides/channels.md b/guides/channels.md
index 9537735d0..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="<%= Routes.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 9db624304..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:
@@ -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
@@ -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
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/errors.md b/guides/errors.md
index b7ff268fe..e44a9e7a9 100644
--- a/guides/errors.md
+++ b/guides/errors.md
@@ -36,7 +36,7 @@ config :hello, HelloWeb.Endpoint,
   debug_errors: false,
   code_reloader: true,
   cache_static_lookup: false,
-  watchers: [node: ["node_modules/webpack/bin/webpack.js", "--mode", "development", "--watch-stdin", "--colors",
+  watchers: [node: ["node_modules/webpack/bin/webpack.js", "--mode", "development", "--watch-stdin",
                     cd: Path.expand("../assets", __DIR__)]]
 ```
 
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 427797a49..147f04828 100644
--- a/installer/templates/phx_single/config/dev.exs
+++ b/installer/templates/phx_single/config/dev.exs
@@ -11,7 +11,7 @@ config :<%= app_name %>, <%= endpoint_module %>,
   debug_errors: true,
   code_reloader: true,
   check_origin: false,
-  watchers: <%= if webpack do %>[node: ["node_modules/webpack/bin/webpack.js", "--mode", "development", "--watch-stdin", "--colors",
+  watchers: <%= if webpack do %>[node: ["node_modules/webpack/bin/webpack.js", "--mode", "development", "--watch-stdin",
                     cd: Path.expand("../assets", __DIR__)]]<% else %>[]<% end %>
 
 # ## SSL Support
@@ -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_umbrella/apps/app_name_web/config/dev.exs b/installer/templates/phx_umbrella/apps/app_name_web/config/dev.exs
index 5c703d9d4..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
@@ -15,7 +15,7 @@ config :<%= web_app_name %>, <%= endpoint_module %>,
   debug_errors: true,
   code_reloader: true,
   check_origin: false,
-  watchers: <%= if webpack do %>[node: ["node_modules/webpack/bin/webpack.js", "--mode", "development", "--watch-stdin", "--colors",
+  watchers: <%= if webpack do %>[node: ["node_modules/webpack/bin/webpack.js", "--mode", "development", "--watch-stdin",
                                         cd: Path.expand("../assets", __DIR__)]]<% else %>[]<% end %>
 
 # ## SSL Support
@@ -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_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/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/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