Repository: phoenixframework/phoenix (elixir)

Issue:
Error handling when route is called from hackney

When route is called from curl/browser to an endpoint that doesn't have like a matching function header. You get the normal error like.

```
[debug] ** (Phoenix.ActionClauseError) no function clause matching in NewPhoenixWeb.PageController.index/2

The following arguments were given to NewPhoenixWeb.PageController.index/2:

    # 1
    %Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{}, before_send: ...

    # 2
    %{}

    (new_phoenix) lib/new_phoenix_web/controllers/page_controller.ex:4: NewPhoenixWeb.PageController.index(%Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{}, before_send: ...
```

But when you use hackney to send through like httpoison to send a request the error returns unreadable. Like the example below.

```
[info] Converted error {:badfun, %Phoenix.ActionClauseError{args: [%Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{}, before_send: [...], arity: 2, clauses: nil, function: :index, kind: nil, module: NewPhoenixWeb.PageController}} to 500 response
[error] #PID<0.440.0> running NewPhoenixWeb.Endpoint (connection #PID<0.439.0>, stream id 1) terminated
Server: sandbox-clone-test.betty5.test:4000 (http)
Request: GET /
** (exit) an exception was raised:
    ** (BadFunctionError) expected a function, got: %Phoenix.ActionClauseError{args: [%Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{}, before_send: ...}
        (elixir) lib/exception.ex:1062: anonymous fn/2 in FunctionClauseError.blame/3
        (elixir) lib/enum.ex:1314: Enum."-map/2-lists^map/1-0-"/2
        (elixir) lib/exception.ex:1061: FunctionClauseError.blame/3
        (phoenix) lib/phoenix/exceptions.ex:59: Phoenix.ActionClauseError.blame/2
        (plug) lib/plug/debugger.ex:197: Plug.Debugger.render/6
        (plug) lib/plug/debugger.ex:147: Plug.Debugger.__catch__/5
```

Pull request:
Fix blame in Phoenix.ActionClauseError
The blame functionality previously used FunctionClauseError.blame/3,
however it is likely that blame/2 was intended to be used.

This could be triggered when requesting a route with invalid params
without the accept header in place.

This closes #3317

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6a0128722..f58ab774f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -207,7 +207,7 @@ of beginning users because expert users have more tools at their disposal to
 help themselves.
 
 The general pattern we use for presenting information is to first introduce a
-small, discreet topic, then write a small amount of code to demonstrate the
+small, discrete topic, then write a small amount of code to demonstrate the
 concept, then verify that the code worked.
 
 In this way, we build from small, easily digestible concepts into more complex
diff --git a/README.md b/README.md
index b572f66b9..fe1214dd2 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 
 ## Getting started
 
-See the official site at http://www.phoenixframework.org/
+See the official site at https://www.phoenixframework.org/
 
 Install the latest version of Phoenix by following the instructions at https://hexdocs.pm/phoenix/installation.html#phoenix
 
diff --git a/RELEASE.md b/RELEASE.md
index cc19fcd0f..babdd4ffe 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -1,17 +1,14 @@
 # Release Instructions
 
-**IMPORTANT**: when building the archive, it must be done in the minimum supported Erlang and Elixir versions.
-
   1. Check related deps for required version bumps and compatibility (`phoenix_ecto`, `phoenix_pubsub_redis`, `phoenix_html`)
-  2. Bump version in related files below
-  3. Update `@phoenix_version` in `installer/lib/phx_new/generator.ex` to "x.y.z"
+  2. Update `phoenix_dep` in `installer/lib/phx_new/generator.ex` to release
+  3. Bump version in related files below
   4. Run tests, commit, push code
   5. Publish `phx_new` and `phoenix` packages and docs after pruning any extraneous uncommitted files
   6. Test installer by generating a new app, running `mix deps.get`, and compiling
-  7. Start -dev version in related files below
-  8. Update `phoenix_dep` in `installer/lib/phx_new/generator.ex` back to git
-  9. Publish to `npm` with `npm publish`
-  10. Replace `master` for `source_url_pattern` in `installer/mix.exs`
+  7. Publish to `npm` with `npm publish`
+  8. Start -dev version in related files below
+  9. Update `phoenix_dep` in `installer/lib/phx_new/generator.ex` back to git
 
 ## Files with version
 
diff --git a/assets/package.json b/assets/package.json
index 587aa1117..f4a2aa834 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -8,7 +8,7 @@
     "type": "git",
     "url": "git://github.com/phoenixframework/phoenix.git"
   },
-  "author": "Chris McCord <chris@chrismccord.com> (http://www.phoenixframework.org)",
+  "author": "Chris McCord <chris@chrismccord.com> (https://www.phoenixframework.org)",
   "devDependencies": {
     "@babel/core": "^7.0.0",
     "@babel/preset-env": "^7.0.0",
diff --git a/guides/deployment/heroku.md b/guides/deployment/heroku.md
index 9d2b6d03a..a823ddb73 100644
--- a/guides/deployment/heroku.md
+++ b/guides/deployment/heroku.md
@@ -13,7 +13,7 @@ Our main goal for this guide is to get a Phoenix application running on Heroku.
 Heroku is a great platform and Elixir performs well on it. However, you may run into limitations if you plan to leverage advanced features provided by Elixir and Phoenix, such as:
 
 - Connections are limited.
-  - Heroku [limits the number of simultaneous connections](https://devcenter.heroku.com/articles/http-routing#request-concurrency) as well as the [duration of each connection](https://devcenter.heroku.com/articles/limits#http-timeouts). It is common to use Elixir for real-time apps which need lots of concurrent, persistent connections, and Phoenix is capable of [handling over 2 million connections on a single server](http://www.phoenixframework.org/blog/the-road-to-2-million-websocket-connections).
+  - Heroku [limits the number of simultaneous connections](https://devcenter.heroku.com/articles/http-routing#request-concurrency) as well as the [duration of each connection](https://devcenter.heroku.com/articles/limits#http-timeouts). It is common to use Elixir for real-time apps which need lots of concurrent, persistent connections, and Phoenix is capable of [handling over 2 million connections on a single server](https://www.phoenixframework.org/blog/the-road-to-2-million-websocket-connections).
 
 - Distributed clustering is not possible.
   - Heroku [firewalls dynos off from one another](https://devcenter.heroku.com/articles/dynos#networking). This means things like [distributed Phoenix channels](https://dockyard.com/blog/2016/01/28/running-elixir-and-phoenix-projects-on-a-cluster-of-nodes) and [distributed tasks](https://elixir-lang.org/getting-started/mix-otp/distributed-tasks.html) will need to rely on something like Redis instead of Elixir's built-in distribution.
diff --git a/guides/ecto.md b/guides/ecto.md
index bde6f1715..3846e1242 100644
--- a/guides/ecto.md
+++ b/guides/ecto.md
@@ -11,7 +11,7 @@ Newly generated Phoenix projects include Ecto with the PostgreSQL adapter by def
 
 For a thorough, general guide for Ecto, check out the [Ecto getting started guide](https://hexdocs.pm/ecto/getting-started.html). For an overview of all Ecto specific mix tasks for Phoenix, see the [mix tasks guide](phoenix_mix_tasks.html#ecto-specific-mix-tasks).
 
-This guide assumes that we have generated our new application with Ecto integration and that we will be using PostgreSQL. For instructions on switching to MySQL, please see the [Using MySQL](ecto.html#using-mysql) section.
+This guide assumes that we have generated our new application with Ecto integration and that we will be using PostgreSQL. For instructions on switching to MySQL, please see the [Using MySQL](#using-mysql) section.
 
 The default Postgres configuration has a superuser account with username 'postgres' and the password 'postgres'. If you take a look at the file `config/dev.exs`, you'll see that Phoenix works off this assumption. If you don't have this account already setup on your machine, you can connect to your postgres instance by typing `psql` and then entering the following commands:
 
diff --git a/guides/errors.md b/guides/errors.md
index fb5536ec3..a4de46e96 100644
--- a/guides/errors.md
+++ b/guides/errors.md
@@ -72,7 +72,7 @@ defmodule Phoenix.Router do
 end
 ```
 
-Plug provides a protocol called `Plug.Exception` specifically for adding a status to exception structs.
+Plug provides a protocol called `Plug.Exception` where we are able to customize the status and add actions that exception structs can returns on the debug error page.
 
 If we wanted to supply a status of 404 for an `Ecto.NoResultsError`, we could do it by defining an implementation for the `Plug.Exception` protocol like this:
 
@@ -83,3 +83,20 @@ end
 ```
 
 Note that this is just an example: Phoenix [already does this](https://github.com/phoenixframework/phoenix_ecto/blob/master/lib/phoenix_ecto/plug.ex) for `Ecto.NoResultsError`, so you don't have to.
+
+#### Actions
+
+Actions are functions that can be triggered by the error page, it is basically a list of maps defining a `label` and a `handler` to be executed.
+It is rendered in the error page as a collection of buttons and follows the format of: `[%{label: String.t(), handler: {module(), function :: atom(), args :: []}}]`.
+
+If we wanted to return some actions for an `Ecto.NoResultsError` we would implement `Plug.Exception` like this:
+
+```elixir
+defimpl Plug.Exception, for: Ecto.NoResultsError do
+  def status(_exception), do: 404
+  def actions(_exception), do: [%{
+      label: "Run seeds",
+      handler: {Code, :eval_file, "priv/repo/seeds.exs"}
+    }]
+end
+```
\ No newline at end of file
diff --git a/guides/phoenix_mix_tasks.md b/guides/phoenix_mix_tasks.md
index ba4ffc4ee..7d8989592 100644
--- a/guides/phoenix_mix_tasks.md
+++ b/guides/phoenix_mix_tasks.md
@@ -874,7 +874,7 @@ defmodule Mix.Tasks.Hello.Greeting do
   """
 
   def run(_args) do
-    Mix.shell.info("Greetings from the Hello Phoenix Application!")
+    Mix.shell().info("Greetings from the Hello Phoenix Application!")
   end
 
   # We can define other functions as needed here.
@@ -892,7 +892,7 @@ The `@shortdoc` module attribute holds a string which will describe our task whe
 
 `@moduledoc` serves the same function that it does in any module. It's where we can put long-form documentation and doctests, if we have any.
 
-The `run/1` function is the critical heart of any mix task. It's the function that does all the work when users invoke our task. In ours, all we do is send a greeting from our app, but we can implement our `run/1` function to do whatever we need it to. Note that `Mix.shell.info/1` is the preferred way to print text back out to the user.
+The `run/1` function is the critical heart of any mix task. It's the function that does all the work when users invoke our task. In ours, all we do is send a greeting from our app, but we can implement our `run/1` function to do whatever we need it to. Note that `Mix.shell().info/1` is the preferred way to print text back out to the user.
 
 Of course, our task is just a module, so we can define other private functions as needed to support our `run/1` function.
 
@@ -928,7 +928,7 @@ If you want to make your new mix task to use your application's infrastructure,
   . . .
   def run(_args) do
     Mix.Task.run("app.start")
-    Mix.shell.info("Now I have access to Repo and other goodies!")
+    Mix.shell().info("Now I have access to Repo and other goodies!")
   end
   . . .
 ```
diff --git a/installer/lib/mix/tasks/phx.new.ex b/installer/lib/mix/tasks/phx.new.ex
index 6fb809a7b..53f7e7b0a 100644
--- a/installer/lib/mix/tasks/phx.new.ex
+++ b/installer/lib/mix/tasks/phx.new.ex
@@ -98,7 +98,7 @@ defmodule Mix.Tasks.Phx.New do
              gettext: :boolean, umbrella: :boolean, verbose: :boolean]
 
   def run([version]) when version in ~w(-v --version) do
-    Mix.shell.info("Phoenix v#{@version}")
+    Mix.shell().info("Phoenix v#{@version}")
   end
 
   def run(argv) do
@@ -142,7 +142,7 @@ defmodule Mix.Tasks.Phx.New do
 
   defp prompt_to_install_deps(%Project{} = project, generator, path) do
     path = Map.fetch!(project, path)
-    install? = Mix.shell.yes?("\nFetch and install dependencies?")
+    install? = Mix.shell().yes?("\nFetch and install dependencies?")
     cd_step = ["$ cd #{relative_app_path(path)}"]
 
     maybe_cd(path, fn ->
@@ -204,7 +204,7 @@ defmodule Mix.Tasks.Phx.New do
   end
 
   defp print_webpack_info(_project, _gen) do
-    Mix.shell.info """
+    Mix.shell().info """
     Phoenix uses an optional assets build tool called webpack
     that requires node.js and npm. Installation instructions for
     node.js, which includes npm, can be found at http://nodejs.org.
@@ -216,7 +216,7 @@ defmodule Mix.Tasks.Phx.New do
   end
 
   defp print_missing_steps(steps) do
-    Mix.shell.info """
+    Mix.shell().info """
 
     We are almost there! The following steps are missing:
 
@@ -226,7 +226,7 @@ defmodule Mix.Tasks.Phx.New do
 
   defp print_ecto_info(Web), do: :ok
   defp print_ecto_info(_gen) do
-    Mix.shell.info """
+    Mix.shell().info """
     Then configure your database in config/dev.exs and run:
 
         $ mix ecto.create
@@ -234,14 +234,14 @@ defmodule Mix.Tasks.Phx.New do
   end
 
   defp print_mix_info(Ecto) do
-    Mix.shell.info """
+    Mix.shell().info """
     You can run your app inside IEx (Interactive Elixir) as:
 
         $ iex -S mix
     """
   end
   defp print_mix_info(_gen) do
-    Mix.shell.info """
+    Mix.shell().info """
     Start your Phoenix app with:
 
         $ mix phx.server
@@ -261,15 +261,6 @@ defmodule Mix.Tasks.Phx.New do
 
   ## Helpers
 
-  @doc false
-  def recompile(regex) do
-    if Code.ensure_loaded?(Regex) and function_exported?(Regex, :recompile!, 1) do
-      apply(Regex, :recompile!, [regex])
-    else
-      regex
-    end
-  end
-
   defp maybe_cmd(project, cmd, should_run?, can_run?) do
     cond do
       should_run? && can_run? ->
@@ -282,8 +273,8 @@ defmodule Mix.Tasks.Phx.New do
   end
 
   defp cmd(%Project{} = project, cmd) do
-    Mix.shell.info [:green, "* running ", :reset, cmd]
-    case Mix.shell.cmd(cmd, cmd_opts(project)) do
+    Mix.shell().info [:green, "* running ", :reset, cmd]
+    case Mix.shell().cmd(cmd, cmd_opts(project)) do
       0 ->
         []
       _ ->
@@ -300,7 +291,7 @@ defmodule Mix.Tasks.Phx.New do
   end
 
   defp check_app_name!(name, from_app_flag) do
-    unless name =~ recompile(~r/^[a-z][\w_]*$/) do
+    unless name =~ Regex.recompile!(~r/^[a-z][\w_]*$/) do
       extra =
         if !from_app_flag do
           ". The application name is inferred from the path, if you'd like to " <>
@@ -315,7 +306,7 @@ defmodule Mix.Tasks.Phx.New do
   end
 
   defp check_module_name_validity!(name) do
-    unless inspect(name) =~ recompile(~r/^[A-Z]\w*(\.[A-Z]\w*)*$/) do
+    unless inspect(name) =~ Regex.recompile!(~r/^[A-Z]\w*(\.[A-Z]\w*)*$/) do
       Mix.raise "Module name must be a valid Elixir alias (for example: Foo.Bar), got: #{inspect name}"
     end
   end
@@ -335,7 +326,7 @@ defmodule Mix.Tasks.Phx.New do
   end
 
   defp check_directory_existence!(path) do
-    if File.dir?(path) and not Mix.shell.yes?("The directory #{path} already exists. Are you sure you want to continue?") do
+    if File.dir?(path) and not Mix.shell().yes?("The directory #{path} already exists. Are you sure you want to continue?") do
       Mix.raise "Please select another directory for installation."
     end
   end
diff --git a/installer/lib/phx_new/generator.ex b/installer/lib/phx_new/generator.ex
index 2c06c3060..4cca3f23b 100644
--- a/installer/lib/phx_new/generator.ex
+++ b/installer/lib/phx_new/generator.ex
@@ -4,7 +4,7 @@ defmodule Phx.New.Generator do
   alias Phx.New.{Project}
 
   @phoenix Path.expand("../..", __DIR__)
-  @phoenix_version Version.parse!("1.4.0")
+  @phoenix_version Version.parse!(Mix.Project.config[:version])
 
   @callback prepare_project(Project.t) :: Project.t
   @callback generate(Project.t) :: Project.t
diff --git a/installer/lib/phx_new/project.ex b/installer/lib/phx_new/project.ex
index e59d85215..5ed677b4c 100644
--- a/installer/lib/phx_new/project.ex
+++ b/installer/lib/phx_new/project.ex
@@ -61,7 +61,7 @@ defmodule Phx.New.Project do
   end
 
   defp expand_path_with_bindings(path, %Project{} = project) do
-    Regex.replace(Mix.Tasks.Phx.New.recompile(~r/:[a-zA-Z0-9_]+/), path, fn ":" <> key, _ ->
+    Regex.replace(Regex.recompile!(~r/:[a-zA-Z0-9_]+/), path, fn ":" <> key, _ ->
         project |> Map.fetch!(:"#{key}") |> to_string()
     end)
   end
diff --git a/installer/mix.exs b/installer/mix.exs
index bfc57d8f1..692725dc6 100644
--- a/installer/mix.exs
+++ b/installer/mix.exs
@@ -1,7 +1,7 @@
 defmodule Phx.New.MixProject do
   use Mix.Project
 
-  @version "1.4.0"
+  @version "1.5.0-dev"
   @github_path "phoenixframework/phoenix"
   @url "https://github.com/#{@github_path}"
 
@@ -10,7 +10,7 @@ defmodule Phx.New.MixProject do
       app: :phx_new,
       start_permanent: Mix.env() == :prod,
       version: @version,
-      elixir: "~> 1.5",
+      elixir: "~> 1.6",
       deps: deps(),
       package: [
         maintainers: [
@@ -26,7 +26,7 @@ defmodule Phx.New.MixProject do
 
       source_url: @url,
       docs: docs(),
-      homepage_url: "http://www.phoenixframework.org",
+      homepage_url: "https://www.phoenixframework.org",
       description: """
       Phoenix framework project generator.
 
diff --git a/installer/templates/phx_single/README.md b/installer/templates/phx_single/README.md
index 95553d6c0..2f263fda0 100644
--- a/installer/templates/phx_single/README.md
+++ b/installer/templates/phx_single/README.md
@@ -13,7 +13,7 @@ Ready to run in production? Please [check our deployment guides](https://hexdocs
 
 ## Learn more
 
-  * Official website: http://www.phoenixframework.org/
+  * Official website: https://www.phoenixframework.org/
   * Guides: https://hexdocs.pm/phoenix/overview.html
   * Docs: https://hexdocs.pm/phoenix
   * Mailing list: http://groups.google.com/group/phoenix-talk
diff --git a/installer/templates/phx_single/mix.exs b/installer/templates/phx_single/mix.exs
index 6f539d93f..567f90a4a 100644
--- a/installer/templates/phx_single/mix.exs
+++ b/installer/templates/phx_single/mix.exs
@@ -38,7 +38,7 @@ defmodule <%= app_module %>.MixProject do
   defp deps do
     [
       <%= phoenix_dep %>,<%= if ecto do %>
-      {:phoenix_ecto, "~> 4.0"},
+      {:phoenix_ecto, "~> 4.1"},
       {:ecto_sql, "~> 3.1"},
       {<%= inspect adapter_app %>, ">= 0.0.0"},<% end %><%= if html do %>
       {:phoenix_html, "~> 2.11"},
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/README.md b/installer/templates/phx_umbrella/apps/app_name_web/README.md
index ba77b2ce3..87cba417c 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/README.md
+++ b/installer/templates/phx_umbrella/apps/app_name_web/README.md
@@ -13,7 +13,7 @@ Ready to run in production? Please [check our deployment guides](https://hexdocs
 
 ## Learn more
 
-  * Official website: http://www.phoenixframework.org/
+  * Official website: https://www.phoenixframework.org/
   * Guides: https://hexdocs.pm/phoenix/overview.html
   * Docs: https://hexdocs.pm/phoenix
   * Mailing list: http://groups.google.com/group/phoenix-talk
diff --git a/installer/templates/phx_web/endpoint.ex b/installer/templates/phx_web/endpoint.ex
index 4ebc796ec..8efd62e8c 100644
--- a/installer/templates/phx_web/endpoint.ex
+++ b/installer/templates/phx_web/endpoint.ex
@@ -20,7 +20,8 @@ defmodule <%= endpoint_module %> do
   if code_reloading? do<%= if html do %>
     socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
     plug Phoenix.LiveReloader<% end %>
-    plug Phoenix.CodeReloader
+    plug Phoenix.CodeReloader<%= if ecto do %>
+    plug Phoenix.Ecto.CheckRepoStatus, otp_app: :<%= web_app_name %><% end %>
   end
 
   plug Plug.RequestId
diff --git a/installer/templates/phx_web/templates/layout/app.html.eex b/installer/templates/phx_web/templates/layout/app.html.eex
index c9a2fba66..15d753f5b 100644
--- a/installer/templates/phx_web/templates/layout/app.html.eex
+++ b/installer/templates/phx_web/templates/layout/app.html.eex
@@ -15,7 +15,7 @@
             <li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
           </ul>
         </nav>
-        <a href="http://phoenixframework.org/" class="phx-logo">
+        <a href="https://phoenixframework.org/" class="phx-logo">
           <img src="<%%= Routes.static_path(@conn, "/images/phoenix.png") %>" alt="Phoenix Framework Logo"/>
         </a>
       </section>
diff --git a/lib/mix/phoenix.ex b/lib/mix/phoenix.ex
index c65691402..47a9e0a11 100644
--- a/lib/mix/phoenix.ex
+++ b/lib/mix/phoenix.ex
@@ -144,7 +144,7 @@ defmodule Mix.Phoenix do
 
   defp app_base(app) do
     case Application.get_env(app, :namespace, app) do
-      ^app -> app |> to_string |> Phoenix.Naming.camelize()
+      ^app -> app |> to_string() |> Phoenix.Naming.camelize()
       mod  -> mod |> inspect()
     end
   end
@@ -153,16 +153,16 @@ defmodule Mix.Phoenix do
   Returns the OTP app from the Mix project configuration.
   """
   def otp_app do
-    Mix.Project.config |> Keyword.fetch!(:app)
+    Mix.Project.config() |> Keyword.fetch!(:app)
   end
 
   @doc """
   Returns all compiled modules in a project.
   """
   def modules do
-    Mix.Project.compile_path
+    Mix.Project.compile_path()
     |> Path.join("*.beam")
-    |> Path.wildcard
+    |> Path.wildcard()
     |> Enum.map(&beam_to_module/1)
   end
 
@@ -324,14 +324,14 @@ defmodule Mix.Phoenix do
     case Enum.filter(file_paths, &File.exists?(&1)) do
       [] -> :ok
       conflicts ->
-        Mix.shell.info"""
+        Mix.shell().info"""
         The following files conflict with new files to be generated:
 
         #{conflicts |> Enum.map(&"  * #{&1}") |> Enum.join("\n")}
 
         See the --web option to namespace similarly named resources
         """
-        unless Mix.shell.yes?("Proceed with interactive overwrite?") do
+        unless Mix.shell().yes?("Proceed with interactive overwrite?") do
           System.halt()
         end
     end
diff --git a/lib/mix/tasks/phx.digest.clean.ex b/lib/mix/tasks/phx.digest.clean.ex
index 55ac8b09a..5ec2e5d46 100644
--- a/lib/mix/tasks/phx.digest.clean.ex
+++ b/lib/mix/tasks/phx.digest.clean.ex
@@ -49,9 +49,9 @@ defmodule Mix.Tasks.Phx.Digest.Clean do
         # true. In case it's not true, build structure is mostly a no-op, so we
         # are fine.
         Mix.Project.build_structure()
-        Mix.shell.info [:green, "Clean complete for #{inspect output_path}"]
+        Mix.shell().info [:green, "Clean complete for #{inspect output_path}"]
       {:error, :invalid_path} ->
-        Mix.shell.error "The output path #{inspect output_path} does not exist"
+        Mix.shell().error "The output path #{inspect output_path} does not exist"
     end
   end
 end
diff --git a/lib/mix/tasks/phx.digest.ex b/lib/mix/tasks/phx.digest.ex
index c0f8d3062..9b5ae270a 100644
--- a/lib/mix/tasks/phx.digest.ex
+++ b/lib/mix/tasks/phx.digest.ex
@@ -51,9 +51,9 @@ defmodule Mix.Tasks.Phx.Digest do
         # build_embedded set to true. In case it's not true,
         # build structure is mostly a no-op, so we are fine.
         Mix.Project.build_structure()
-        Mix.shell.info [:green, "Check your digested files at #{inspect output_path}"]
+        Mix.shell().info [:green, "Check your digested files at #{inspect output_path}"]
       {:error, :invalid_path} ->
-        Mix.shell.error "The input path #{inspect input_path} does not exist"
+        Mix.shell().error "The input path #{inspect input_path} does not exist"
     end
   end
 end
diff --git a/lib/mix/tasks/phx.ex b/lib/mix/tasks/phx.ex
index 9ddf6a90d..d460df13c 100644
--- a/lib/mix/tasks/phx.ex
+++ b/lib/mix/tasks/phx.ex
@@ -20,10 +20,10 @@ defmodule Mix.Tasks.Phx do
 
   defp general() do
     Application.ensure_all_started(:phoenix)
-    Mix.shell.info "Phoenix v#{Application.spec(:phoenix, :vsn)}"
-    Mix.shell.info "Productive. Reliable. Fast."
-    Mix.shell.info "A productive web framework that does not compromise speed or maintainability."
-    Mix.shell.info "\nAvailable tasks:\n"
+    Mix.shell().info "Phoenix v#{Application.spec(:phoenix, :vsn)}"
+    Mix.shell().info "Productive. Reliable. Fast."
+    Mix.shell().info "A productive web framework that does not compromise speed or maintainability."
+    Mix.shell().info "\nAvailable tasks:\n"
     Mix.Tasks.Help.run(["--search", "phx."])
   end
 end
diff --git a/lib/mix/tasks/phx.gen.channel.ex b/lib/mix/tasks/phx.gen.channel.ex
index 5179f5ab6..420b13774 100644
--- a/lib/mix/tasks/phx.gen.channel.ex
+++ b/lib/mix/tasks/phx.gen.channel.ex
@@ -42,7 +42,7 @@ defmodule Mix.Tasks.Phx.Gen.Channel do
       {:eex, "channel_test.exs", Path.join(test_prefix, "channels/#{binding[:path]}_channel_test.exs")},
     ]
 
-    Mix.shell.info """
+    Mix.shell().info """
 
     Add the channel to your `#{Mix.Phoenix.web_path(context_app, "channels/user_socket.ex")}` handler, for example:
 
diff --git a/lib/mix/tasks/phx.gen.context.ex b/lib/mix/tasks/phx.gen.context.ex
index 4ac4f049e..19aea6c47 100644
--- a/lib/mix/tasks/phx.gen.context.ex
+++ b/lib/mix/tasks/phx.gen.context.ex
@@ -77,7 +77,7 @@ defmodule Mix.Tasks.Phx.Gen.Context do
 
   @doc false
   def run(args) do
-    if Mix.Project.umbrella? do
+    if Mix.Project.umbrella?() do
       Mix.raise "mix phx.gen.context can only be run inside an application directory"
     end
 
@@ -171,7 +171,7 @@ defmodule Mix.Tasks.Phx.Gen.Context do
     if String.contains?(file, content_to_inject) do
       :ok
     else
-      Mix.shell.info([:green, "* injecting ", :reset, Path.relative_to_cwd(file_path)])
+      Mix.shell().info([:green, "* injecting ", :reset, Path.relative_to_cwd(file_path)])
 
       file
       |> String.trim_trailing()
@@ -247,7 +247,7 @@ defmodule Mix.Tasks.Phx.Gen.Context do
       function_count = Context.function_count(context)
       file_count = Context.file_count(context)
 
-      Mix.shell.info """
+      Mix.shell().info """
       You are generating into an existing context.
 
       The #{inspect context.module} context currently has #{function_count} functions and \
@@ -264,7 +264,7 @@ defmodule Mix.Tasks.Phx.Gen.Context do
 
       If you are not sure, prefer creating a new context over adding to the existing one.
       """
-      unless Mix.shell.yes?("Would you like to proceed?") do
+      unless Mix.shell().yes?("Would you like to proceed?") do
         System.halt()
       end
     end
diff --git a/lib/mix/tasks/phx.gen.html.ex b/lib/mix/tasks/phx.gen.html.ex
index 826b8585b..40405f842 100644
--- a/lib/mix/tasks/phx.gen.html.ex
+++ b/lib/mix/tasks/phx.gen.html.ex
@@ -102,7 +102,7 @@ defmodule Mix.Tasks.Phx.Gen.Html do
 
   @doc false
   def run(args) do
-    if Mix.Project.umbrella? do
+    if Mix.Project.umbrella?() do
       Mix.raise "mix phx.gen.html can only be run inside an application directory"
     end
 
@@ -161,7 +161,7 @@ defmodule Mix.Tasks.Phx.Gen.Html do
   @doc false
   def print_shell_instructions(%Context{schema: schema, context_app: ctx_app} = context) do
     if schema.web_namespace do
-      Mix.shell.info """
+      Mix.shell().info """
 
       Add the resource to your #{schema.web_namespace} :browser scope in #{Mix.Phoenix.web_path(ctx_app)}/router.ex:
 
@@ -172,7 +172,7 @@ defmodule Mix.Tasks.Phx.Gen.Html do
           end
       """
     else
-      Mix.shell.info """
+      Mix.shell().info """
 
       Add the resource to your browser scope in #{Mix.Phoenix.web_path(ctx_app)}/router.ex:
 
diff --git a/lib/mix/tasks/phx.gen.json.ex b/lib/mix/tasks/phx.gen.json.ex
index b42e9606b..f6b28da2e 100644
--- a/lib/mix/tasks/phx.gen.json.ex
+++ b/lib/mix/tasks/phx.gen.json.ex
@@ -102,7 +102,7 @@ defmodule Mix.Tasks.Phx.Gen.Json do
 
   @doc false
   def run(args) do
-    if Mix.Project.umbrella? do
+    if Mix.Project.umbrella?() do
       Mix.raise "mix phx.gen.json can only be run inside an application directory"
     end
 
@@ -159,7 +159,7 @@ defmodule Mix.Tasks.Phx.Gen.Json do
   @doc false
   def print_shell_instructions(%Context{schema: schema, context_app: ctx_app} = context) do
     if schema.web_namespace do
-      Mix.shell.info """
+      Mix.shell().info """
 
       Add the resource to your #{schema.web_namespace} :api scope in #{Mix.Phoenix.web_path(ctx_app)}/router.ex:
 
@@ -170,7 +170,7 @@ defmodule Mix.Tasks.Phx.Gen.Json do
           end
       """
     else
-      Mix.shell.info """
+      Mix.shell().info """
 
       Add the resource to your :api scope in #{Mix.Phoenix.web_path(ctx_app)}/router.ex:
 
diff --git a/lib/mix/tasks/phx.gen.presence.ex b/lib/mix/tasks/phx.gen.presence.ex
index d392c18da..e36389ced 100644
--- a/lib/mix/tasks/phx.gen.presence.ex
+++ b/lib/mix/tasks/phx.gen.presence.ex
@@ -41,7 +41,7 @@ defmodule Mix.Tasks.Phx.Gen.Presence do
 
     Mix.Phoenix.copy_from paths(), "priv/templates/phx.gen.presence", binding, files
 
-    Mix.shell.info """
+    Mix.shell().info """
 
     Add your new module to your supervision tree,
     in lib/#{otp_app}/application.ex:
diff --git a/lib/mix/tasks/phx.gen.schema.ex b/lib/mix/tasks/phx.gen.schema.ex
index ec422e594..fc87d5761 100644
--- a/lib/mix/tasks/phx.gen.schema.ex
+++ b/lib/mix/tasks/phx.gen.schema.ex
@@ -162,7 +162,7 @@ defmodule Mix.Tasks.Phx.Gen.Schema do
   @doc false
   def print_shell_instructions(%Schema{} = schema) do
     if schema.migration? do
-      Mix.shell.info """
+      Mix.shell().info """
 
       Remember to update your repository by running migrations:
 
diff --git a/lib/mix/tasks/phx.gen.secret.ex b/lib/mix/tasks/phx.gen.secret.ex
index aa43f0f25..2bbe426d3 100644
--- a/lib/mix/tasks/phx.gen.secret.ex
+++ b/lib/mix/tasks/phx.gen.secret.ex
@@ -14,7 +14,7 @@ defmodule Mix.Tasks.Phx.Gen.Secret do
 
   @doc false
   def run([]),    do: run(["64"])
-  def run([int]), do: int |> parse!() |> random_string() |> Mix.shell.info()
+  def run([int]), do: int |> parse!() |> random_string() |> Mix.shell().info()
   def run([_|_]), do: invalid_args!()
 
   defp parse!(int) do
diff --git a/lib/mix/tasks/phx.routes.ex b/lib/mix/tasks/phx.routes.ex
index d08ce007f..862522b36 100644
--- a/lib/mix/tasks/phx.routes.ex
+++ b/lib/mix/tasks/phx.routes.ex
@@ -38,7 +38,7 @@ defmodule Mix.Tasks.Phx.Routes do
 
     router_mod
     |> ConsoleFormatter.format(endpoint(opts[:endpoint], base))
-    |> Mix.shell.info()
+    |> Mix.shell().info()
   end
 
   defp endpoint(nil, base) do
diff --git a/lib/mix/tasks/phx.server.ex b/lib/mix/tasks/phx.server.ex
index 426f5291f..6db1b3ba5 100644
--- a/lib/mix/tasks/phx.server.ex
+++ b/lib/mix/tasks/phx.server.ex
@@ -40,6 +40,6 @@ defmodule Mix.Tasks.Phx.Server do
   end
 
   defp iex_running? do
-    Code.ensure_loaded?(IEx) and IEx.started?
+    Code.ensure_loaded?(IEx) and IEx.started?()
   end
 end
diff --git a/lib/phoenix/channel.ex b/lib/phoenix/channel.ex
index a30d5c572..bb9df984e 100644
--- a/lib/phoenix/channel.ex
+++ b/lib/phoenix/channel.ex
@@ -198,7 +198,7 @@ defmodule Phoenix.Channel do
   ## Subscribing to external topics
 
   Sometimes you may need to programmatically subscribe a socket to external
-  topics in addition to the the internal `socket.topic`. For example,
+  topics in addition to the internal `socket.topic`. For example,
   imagine you have a bidding system where a remote client dynamically sets
   preferences on products they want to receive bidding notifications on.
   Instead of requiring a unique channel process and topic per
diff --git a/lib/phoenix/code_reloader/proxy.ex b/lib/phoenix/code_reloader/proxy.ex
index 764eb519e..fc203606f 100644
--- a/lib/phoenix/code_reloader/proxy.ex
+++ b/lib/phoenix/code_reloader/proxy.ex
@@ -37,7 +37,7 @@ defmodule Phoenix.CodeReloader.Proxy do
         put_chars(from, reply, apply(m, f, as), output)
 
       {:io_request, _from, _reply, _request} = msg ->
-        send(Process.group_leader, msg)
+        send(Process.group_leader(), msg)
         {:noreply, output}
 
       _ ->
@@ -46,7 +46,7 @@ defmodule Phoenix.CodeReloader.Proxy do
   end
 
   defp put_chars(from, reply, chars, output) do
-    send(Process.group_leader, {:io_request, from, reply, {:put_chars, chars}})
+    send(Process.group_leader(), {:io_request, from, reply, {:put_chars, chars}})
     {:noreply, output <> IO.chardata_to_string(chars)}
   end
 end
diff --git a/lib/phoenix/code_reloader/server.ex b/lib/phoenix/code_reloader/server.ex
index fc87baa2e..cb72bc061 100644
--- a/lib/phoenix/code_reloader/server.ex
+++ b/lib/phoenix/code_reloader/server.ex
@@ -40,8 +40,8 @@ defmodule Phoenix.CodeReloader.Server do
   end
 
   def handle_call(:check_symlinks, _from, checked?) do
-    if not checked? and Code.ensure_loaded?(Mix.Project) and not Mix.Project.umbrella? do
-      priv_path = "#{Mix.Project.app_path}/priv"
+    if not checked? and Code.ensure_loaded?(Mix.Project) and not Mix.Project.umbrella?() do
+      priv_path = "#{Mix.Project.app_path()}/priv"
 
       case :file.read_link(priv_path) do
         {:ok, _} ->
@@ -50,10 +50,12 @@ defmodule Phoenix.CodeReloader.Server do
         {:error, _} ->
           if can_symlink?() do
             File.rm_rf(priv_path)
-            Mix.Project.build_structure
+            Mix.Project.build_structure()
           else
-            Logger.warn "Phoenix is unable to create symlinks. Phoenix' code reloader will run " <>
-                        "considerably faster if symlinks are allowed." <> os_symlink(:os.type)
+            Logger.warn(
+              "Phoenix is unable to create symlinks. Phoenix' code reloader will run " <>
+                "considerably faster if symlinks are allowed." <> os_symlink(:os.type())
+            )
           end
       end
     end
@@ -65,7 +67,7 @@ defmodule Phoenix.CodeReloader.Server do
     compilers = endpoint.config(:reloadable_compilers)
     reloadable_apps = endpoint.config(:reloadable_apps) || default_reloadable_apps()
     backup = load_backup(endpoint)
-    froms  = all_waiting([from], endpoint)
+    froms = all_waiting([from], endpoint)
 
     {res, out} =
       proxy_io(fn ->
@@ -74,8 +76,9 @@ defmodule Phoenix.CodeReloader.Server do
         catch
           :exit, {:shutdown, 1} ->
             :error
+
           kind, reason ->
-            IO.puts Exception.format(kind, reason, System.stacktrace)
+            IO.puts(Exception.format(kind, reason, System.stacktrace()))
             :error
         end
       end)
@@ -84,6 +87,7 @@ defmodule Phoenix.CodeReloader.Server do
       case res do
         :ok ->
           :ok
+
         :error ->
           write_backup(backup)
           {:error, out}
@@ -103,17 +107,19 @@ defmodule Phoenix.CodeReloader.Server do
   end
 
   defp default_reloadable_apps() do
-    if Mix.Project.umbrella? do
-      Enum.map(Mix.Dep.Umbrella.cached, &(&1.app))
+    if Mix.Project.umbrella?() do
+      Enum.map(Mix.Dep.Umbrella.cached(), & &1.app)
     else
       [Mix.Project.config()[:app]]
     end
   end
 
   defp os_symlink({:win32, _}),
-    do: " On Windows, the lack of symlinks may even cause empty assets to be served. " <>
+    do:
+      " On Windows, the lack of symlinks may even cause empty assets to be served. " <>
         "Luckily, you can address this issue by starting your Windows terminal at least " <>
         "once with \"Run as Administrator\" and then running your Phoenix application."
+
   defp os_symlink(_),
     do: ""
 
@@ -140,12 +146,14 @@ defmodule Phoenix.CodeReloader.Server do
     |> :code.which()
     |> read_backup()
   end
+
   defp read_backup(path) when is_list(path) do
     case File.read(path) do
       {:ok, binary} -> {:ok, path, binary}
       _ -> :error
     end
   end
+
   defp read_backup(_path), do: :error
 
   defp write_backup({:ok, path, file}), do: File.write!(path, file)
@@ -160,27 +168,29 @@ defmodule Phoenix.CodeReloader.Server do
   end
 
   defp mix_compile({:module, Mix.Task}, compilers, apps_to_reload) do
-    mix_compile_deps(Mix.Dep.cached, apps_to_reload, compilers)
+    mix_compile_deps(Mix.Dep.cached(), apps_to_reload, compilers)
     mix_compile_project(Mix.Project.config()[:app], apps_to_reload, compilers)
   end
+
   defp mix_compile({:error, _reason}, _, _) do
     raise "the Code Reloader is enabled but Mix is not available. If you want to " <>
-          "use the Code Reloader in production or inside an escript, you must add " <>
-          ":mix to your applications list. Otherwise, you must disable code reloading " <>
-          "in such environments"
+            "use the Code Reloader in production or inside an escript, you must add " <>
+            ":mix to your applications list. Otherwise, you must disable code reloading " <>
+            "in such environments"
   end
 
   defp mix_compile_deps(deps, apps_to_reload, compilers) do
     for dep <- deps, dep.app in apps_to_reload do
-      Mix.Dep.in_dependency dep, fn _ ->
+      Mix.Dep.in_dependency(dep, fn _ ->
         mix_compile_unless_stale_config(compilers)
-      end
+      end)
     end
 
     :ok
   end
 
   defp mix_compile_project(nil, _, _), do: :ok
+
   defp mix_compile_project(app, apps_to_reload, compilers) do
     if app in apps_to_reload do
       mix_compile_unless_stale_config(compilers)
@@ -190,8 +200,8 @@ defmodule Phoenix.CodeReloader.Server do
   end
 
   defp mix_compile_unless_stale_config(compilers) do
-    manifests = Mix.Tasks.Compile.Elixir.manifests
-    configs   = Mix.Project.config_files
+    manifests = Mix.Tasks.Compile.Elixir.manifests()
+    configs = Mix.Project.config_files()
 
     case Mix.Utils.extract_stale(configs, manifests) do
       [] ->
@@ -199,18 +209,18 @@ defmodule Phoenix.CodeReloader.Server do
 
       files ->
         raise """
-        could not compile application: #{Mix.Project.config[:app]}.
+        could not compile application: #{Mix.Project.config()[:app]}.
 
         You must restart your server after changing the following config or lib files:
 
           * #{Enum.map_join(files, "\n  * ", &Path.relative_to_cwd/1)}
 
         """
-     end
-   end
+    end
+  end
 
   defp mix_compile(compilers) do
-    all = Mix.Project.config[:compilers] || Mix.compilers
+    all = Mix.Project.config()[:compilers] || Mix.compilers()
 
     compilers =
       for compiler <- compilers, compiler in all do
@@ -220,7 +230,7 @@ defmodule Phoenix.CodeReloader.Server do
 
     # We call build_structure mostly for Windows so new
     # assets in priv are copied to the build directory.
-    Mix.Project.build_structure
+    Mix.Project.build_structure()
     results = Enum.map(compilers, &Mix.Task.run("compile.#{&1}", []))
 
     # Results are either {:ok, _} | {:error, _}, {:noop, _} or
@@ -240,11 +250,11 @@ defmodule Phoenix.CodeReloader.Server do
   end
 
   defp consolidate_protocols? do
-    Mix.Project.config[:consolidate_protocols]
+    Mix.Project.config()[:consolidate_protocols]
   end
 
   defp proxy_io(fun) do
-    original_gl = Process.group_leader
+    original_gl = Process.group_leader()
     {:ok, proxy_gl} = Proxy.start()
     Process.group_leader(self(), proxy_gl)
 
diff --git a/lib/phoenix/endpoint.ex b/lib/phoenix/endpoint.ex
index a0e212866..81ebff427 100644
--- a/lib/phoenix/endpoint.ex
+++ b/lib/phoenix/endpoint.ex
@@ -139,7 +139,7 @@ defmodule Phoenix.Endpoint do
       will be referenced from `System.get_env("HOST")` at runtime.
 
       The `:scheme` option accepts `"http"` and `"https"` values. Default value
-      is infered from top level `:http` or `:https` option. It is useful
+      is inferred from top level `:http` or `:https` option. It is useful
       when hosting Phoenix behind a load balancer or reverse proxy and
       terminating SSL there.
 
@@ -183,7 +183,7 @@ defmodule Phoenix.Endpoint do
           ]
 
     * `:pubsub_server` - the name of the pubsub server to use in channels
-      and via the Endpoint broadcast funtions. The PubSub server is typically
+      and via the Endpoint broadcast functions. The PubSub server is typically
       started in your supervision tree.
 
   ### Adapter configuration
diff --git a/lib/phoenix/endpoint/cowboy2_handler.ex b/lib/phoenix/endpoint/cowboy2_handler.ex
index 50fcadcf0..c478c75c4 100644
--- a/lib/phoenix/endpoint/cowboy2_handler.ex
+++ b/lib/phoenix/endpoint/cowboy2_handler.ex
@@ -46,7 +46,7 @@ defmodule Phoenix.Endpoint.Cowboy2Handler do
     end
   catch
     :error, value ->
-      case System.stacktrace do
+      case System.stacktrace() do
         # Maybe the handler is not available because the code is being recompiled.
         # Sync with the code reloader and retry once.
         [{^endpoint, :__handler__, _, _} | _] when value == :undef and retry? ->
diff --git a/lib/phoenix/exceptions.ex b/lib/phoenix/exceptions.ex
index 52c7ed150..c4ad363b2 100644
--- a/lib/phoenix/exceptions.ex
+++ b/lib/phoenix/exceptions.ex
@@ -56,7 +56,7 @@ defmodule Phoenix.ActionClauseError do
     {exception, stacktrace} =
       exception
       |> Map.put(:__struct__, FunctionClauseError)
-      |> FunctionClauseError.blame(exception, stacktrace)
+      |> FunctionClauseError.blame(stacktrace)
 
     exception = Map.put(exception, :__struct__, __MODULE__)
 
@@ -66,4 +66,5 @@ end
 
 defimpl Plug.Exception, for: Phoenix.ActionClauseError do
   def status(_), do: 400
+  def actions(_), do: []
 end
diff --git a/lib/phoenix/router/helpers.ex b/lib/phoenix/router/helpers.ex
index 0cb323b96..fd305e824 100644
--- a/lib/phoenix/router/helpers.ex
+++ b/lib/phoenix/router/helpers.ex
@@ -116,23 +116,23 @@ defmodule Phoenix.Router.Helpers do
 
     defhelper = quote @anno do
       defhelper = fn helper, vars, opts, bins, segs ->
-        def unquote(:"#{helper}_path")(conn_or_endpoint, unquote(opts), unquote_splicing(vars)) do
-          unquote(:"#{helper}_path")(conn_or_endpoint, unquote(opts), unquote_splicing(vars), [])
+        def unquote(:"#{helper}_path")(conn_or_endpoint, unquote(Macro.escape(opts)), unquote_splicing(vars)) do
+          unquote(:"#{helper}_path")(conn_or_endpoint, unquote(Macro.escape(opts)), unquote_splicing(vars), [])
         end
 
-        def unquote(:"#{helper}_path")(conn_or_endpoint, unquote(opts), unquote_splicing(vars), params)
+        def unquote(:"#{helper}_path")(conn_or_endpoint, unquote(Macro.escape(opts)), unquote_splicing(vars), params)
             when is_list(params) or is_map(params) do
           path(conn_or_endpoint, segments(unquote(segs), params, unquote(bins),
-                {unquote(helper), unquote(opts), unquote(Enum.map(vars, &Macro.to_string/1))}))
+                {unquote(helper), unquote(Macro.escape(opts)), unquote(Enum.map(vars, &Macro.to_string/1))}))
         end
 
-        def unquote(:"#{helper}_url")(conn_or_endpoint, unquote(opts), unquote_splicing(vars)) do
-          unquote(:"#{helper}_url")(conn_or_endpoint, unquote(opts), unquote_splicing(vars), [])
+        def unquote(:"#{helper}_url")(conn_or_endpoint, unquote(Macro.escape(opts)), unquote_splicing(vars)) do
+          unquote(:"#{helper}_url")(conn_or_endpoint, unquote(Macro.escape(opts)), unquote_splicing(vars), [])
         end
 
-        def unquote(:"#{helper}_url")(conn_or_endpoint, unquote(opts), unquote_splicing(vars), params)
+        def unquote(:"#{helper}_url")(conn_or_endpoint, unquote(Macro.escape(opts)), unquote_splicing(vars), params)
             when is_list(params) or is_map(params) do
-          url(conn_or_endpoint) <> unquote(:"#{helper}_path")(conn_or_endpoint, unquote(opts), unquote_splicing(vars), params)
+          url(conn_or_endpoint) <> unquote(:"#{helper}_path")(conn_or_endpoint, unquote(Macro.escape(opts)), unquote_splicing(vars), params)
         end
       end
     end
@@ -166,7 +166,7 @@ defmodule Phoenix.Router.Helpers do
 
         defp raise_route_error(unquote(helper), suffix, arity, action, params) do
           Phoenix.Router.Helpers.raise_route_error(__MODULE__, "#{unquote(helper)}_#{suffix}",
-                                                   arity, action, unquote(routes), params)
+                                                   arity, action, unquote(Macro.escape(routes)), params)
         end
       end
     end
@@ -297,7 +297,7 @@ defmodule Phoenix.Router.Helpers do
       defhelper.(
         unquote(helper),
         unquote(Macro.escape(vars)),
-        unquote(opts),
+        unquote(Macro.escape(opts)),
         unquote(Macro.escape(bins)),
         unquote(Macro.escape(segs))
       )
diff --git a/lib/phoenix/router/resource.ex b/lib/phoenix/router/resource.ex
index 043d2ddf3..ca90feb51 100644
--- a/lib/phoenix/router/resource.ex
+++ b/lib/phoenix/router/resource.ex
@@ -21,7 +21,7 @@ defmodule Phoenix.Router.Resource do
     * `:collection` - the context for collection routes
 
   """
-  defstruct [:path, :actions, :param, :route, :controller, :route, :member, :collection, :singleton]
+  defstruct [:path, :actions, :param, :route, :controller, :member, :collection, :singleton]
   @type t :: %Resource{}
 
   @doc """
diff --git a/lib/phoenix/socket.ex b/lib/phoenix/socket.ex
index 4525b2690..8c219cbeb 100644
--- a/lib/phoenix/socket.ex
+++ b/lib/phoenix/socket.ex
@@ -77,7 +77,7 @@ defmodule Phoenix.Socket do
 
     * `:partitions` - each channel is spawned under a supervisor.
       This option controls how many supervisors will be spawned
-      to handlle channels. Defaults to the number of cores.
+      to handle channels. Defaults to the number of cores.
 
   ## Garbage collection
 
@@ -108,7 +108,7 @@ defmodule Phoenix.Socket do
   the original message. Both data-types also include a join_ref that
   uniquely identifes the currently joined channel.
 
-  The `Phoenix.Socket` implementation may also sent special messages
+  The `Phoenix.Socket` implementation may also send special messages
   and replies:
 
     * `"phx_error"` - in case of errors, such as a channel process
@@ -311,8 +311,8 @@ defmodule Phoenix.Socket do
 
   ## Examples
 
-  iex> assign(socket, :name, "Elixir")
-  iex> assign(socket, name: "Elixir", logo: "💧")
+      iex> assign(socket, :name, "Elixir")
+      iex> assign(socket, name: "Elixir", logo: "💧")
   """
   def assign(%Socket{} = socket, key, value) do
     assign(socket, [{key, value}])
diff --git a/lib/phoenix/template.ex b/lib/phoenix/template.ex
index 1ea100402..ef3c38c8f 100644
--- a/lib/phoenix/template.ex
+++ b/lib/phoenix/template.ex
@@ -116,7 +116,7 @@ defmodule Phoenix.Template do
     def message(exception) do
       "Could not render #{inspect exception.template} for #{inspect exception.module}, "
         <> "please define a matching clause for render/2 or define a template at "
-        <> "#{inspect Path.relative_to_cwd exception.root}. "
+        <> "#{inspect Path.join(Path.relative_to_cwd(exception.root), exception.pattern)}. "
         <> available_templates(exception.available)
         <> "\nAssigns:\n\n"
         <> inspect(exception.assigns)
diff --git a/mix.exs b/mix.exs
index 9a01e7995..21ec926c9 100644
--- a/mix.exs
+++ b/mix.exs
@@ -12,7 +12,7 @@ defmodule Phoenix.MixProject do
       package: package(),
       lockfile: lockfile(),
       preferred_cli_env: [docs: :docs],
-      consolidate_protocols: Mix.env != :test,
+      consolidate_protocols: Mix.env() != :test,
       xref: [
         exclude: [
           Ecto.Type,
@@ -23,7 +23,7 @@ defmodule Phoenix.MixProject do
           Plug.Cowboy
         ]
       ],
-      elixirc_paths: elixirc_paths(Mix.env),
+      elixirc_paths: elixirc_paths(Mix.env()),
       name: "Phoenix",
       docs: docs(),
       aliases: aliases(),
diff --git a/package.json b/package.json
index a922680e0..c64b41075 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
     "type": "git",
     "url": "git://github.com/phoenixframework/phoenix.git"
   },
-  "author": "Chris McCord <chris@chrismccord.com> (http://www.phoenixframework.org)",
+  "author": "Chris McCord <chris@chrismccord.com> (https://www.phoenixframework.org)",
   "files": [
     "README.md",
     "LICENSE.md",


Test patch:
diff --git a/installer/test/phx_new_test.exs b/installer/test/phx_new_test.exs
index fc126583a..feab93a83 100644
--- a/installer/test/phx_new_test.exs
+++ b/installer/test/phx_new_test.exs
@@ -133,6 +133,7 @@ defmodule Mix.Tasks.Phx.NewTest do
       assert_file "phx_blog/test/support/data_case.ex", ~r"defmodule PhxBlog.DataCase"
       assert_file "phx_blog/lib/phx_blog_web.ex", ~r"defmodule PhxBlogWeb"
       assert_file "phx_blog/priv/repo/migrations/.formatter.exs", ~r"import_deps: \[:ecto_sql\]"
+      assert_file "phx_blog/lib/phx_blog_web/endpoint.ex", ~r"plug Phoenix.Ecto.CheckRepoStatus, otp_app: :phx_blog"
 
       # Install dependencies?
       assert_received {:mix_shell, :yes?, ["\nFetch and install dependencies?"]}
@@ -178,6 +179,9 @@ defmodule Mix.Tasks.Phx.NewTest do
       # No Ecto
       config = ~r/config :phx_blog, PhxBlog.Repo,/
       refute File.exists?("phx_blog/lib/phx_blog/repo.ex")
+      assert_file "phx_blog/lib/phx_blog_web/endpoint.ex", fn file ->
+        refute file =~ "plug Phoenix.Ecto.CheckRepoStatus, otp_app: :phx_blog"
+      end
 
       # No gettext
       refute_file "phx_blog/lib/phx_blog_web/gettext.ex"
diff --git a/lib/phoenix/test/conn_test.ex b/lib/phoenix/test/conn_test.ex
index 54e4b0e1c..783921378 100644
--- a/lib/phoenix/test/conn_test.ex
+++ b/lib/phoenix/test/conn_test.ex
@@ -369,7 +369,7 @@ defmodule Phoenix.ConnTest do
   end
 
   @doc """
-  Asserts the given status code, that we have an text response and
+  Asserts the given status code, that we have a text response and
   returns the response body if one was set or sent.
 
   ## Examples
@@ -384,7 +384,7 @@ defmodule Phoenix.ConnTest do
   end
 
   @doc """
-  Asserts the given status code, that we have an json response and
+  Asserts the given status code, that we have a json response and
   returns the decoded JSON response if one was set or sent.
 
   ## Examples
@@ -542,7 +542,7 @@ defmodule Phoenix.ConnTest do
   end
 
   @doc """
-  Calls the Endpoint and and the given Router pipelines.
+  Calls the Endpoint and the given Router pipelines.
 
   See `bypass_through/1`.
   """
diff --git a/test/mix/tasks/phx.new_test.exs b/test/mix/tasks/phx.new_test.exs
index 68e6aadd8..ca8b26999 100644
--- a/test/mix/tasks/phx.new_test.exs
+++ b/test/mix/tasks/phx.new_test.exs
@@ -52,7 +52,7 @@ defmodule Mix.Tasks.Phx.NewTest do
           Mix.Task.run "compile", ["--no-deps-check"]
           assert_received {:mix_shell, :info, ["Generated phx_blog app"]}
           refute_received {:mix_shell, :info, ["Generated phoenix app"]}
-          Mix.shell.flush()
+          Mix.shell().flush()
 
           # Adding a new template touches file (through mix)
           File.touch! "lib/phx_blog_web/views/layout_view.ex", @epoch
diff --git a/test/phoenix/router/helpers_test.exs b/test/phoenix/router/helpers_test.exs
index 48dc86c40..51964c65d 100644
--- a/test/phoenix/router/helpers_test.exs
+++ b/test/phoenix/router/helpers_test.exs
@@ -5,7 +5,8 @@ modules = [
   CommentController,
   FileController,
   ProductController,
-  Admin.MessageController
+  Admin.MessageController,
+  SubPlug
 ]
 
 for module <- modules do
@@ -57,6 +58,8 @@ defmodule Phoenix.Router.HelpersTest do
     get "/products", ProductController, :show
     get "/products/:id/:sort", ProductController, :show
     get "/products/:id/:sort/:page", ProductController, :show
+
+    get "/mfa_path", SubPlug, func: {M, :f, [10]}
   end
 
   # Emulate regular endpoint functions
@@ -388,6 +391,10 @@ defmodule Phoenix.Router.HelpersTest do
     assert Helpers.admin_message_path(__MODULE__, :show, 1) == "/admin/new/messages/1"
   end
 
+  test "can pass an {m, f, a} tuple as a plug argument" do
+    assert Helpers.sub_plug_path(__MODULE__, func: {M, :f, [10]}) == "/mfa_path"
+  end
+
   ## Others
 
   defp conn_with_endpoint do
diff --git a/test/phoenix/view_test.exs b/test/phoenix/view_test.exs
index 51bf7a7fa..cd52ca03b 100644
--- a/test/phoenix/view_test.exs
+++ b/test/phoenix/view_test.exs
@@ -29,7 +29,7 @@ defmodule Phoenix.ViewTest do
     catch
       _, _ ->
         info = [file: 'test/fixtures/templates/show.html.eex', line: 1]
-        assert {MyApp.View, :"show.html", 1, info} in System.stacktrace
+        assert {MyApp.View, :"show.html", 1, info} in System.stacktrace()
     else
       _ ->
         flunk "expected rendering to raise"