Repository: phoenixframework/phoenix
Pull request #3359 (2019-04-09 12:34:10)

update heroku.md
make elixir_buildpack.config less "optional" eg. "can be done" vs "is done"


Patch:
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
index 1c87e8848..3e04cc8e9 100644
--- a/ISSUE_TEMPLATE.md
+++ b/ISSUE_TEMPLATE.md
@@ -1,7 +1,7 @@
 <!--
 ### Precheck
 
-* For help and support, use Stack Overflow or the Phoenix Talk mailing list
+* For help and support, use Stack Overflow or the Elixir Forum
 * For proposing a new feature, start a discussion on the Phoenix Core mailing list
 * For bugs, do a quick search and make sure the bug has not yet been reported
 * Ensure that this issue is related to the Phoenix library and not one of the dependencies listed in mix.exs (Ecto, Plug, etc.)
diff --git a/RELEASE.md b/RELEASE.md
index f7b7347c7..cc19fcd0f 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -4,12 +4,12 @@
 
   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_dep` in `installer/lib/phoenix_new.ex` and `installer/lib/phx_new/generator.ex` to "~> version to be released"
+  3. Update `@phoenix_version` in `installer/lib/phx_new/generator.ex` to "x.y.z"
   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/phoenix_new.ex` back to git
+  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`
 
diff --git a/guides/contexts.md b/guides/contexts.md
index 38c800dc5..45a72887f 100644
--- a/guides/contexts.md
+++ b/guides/contexts.md
@@ -61,8 +61,8 @@ username:string:unique
 * creating priv/repo/migrations/20170629175236_create_users.exs
 * creating lib/hello/accounts.ex
 * injecting lib/hello/accounts.ex
-* creating test/hello/accounts/accounts_test.exs
-* injecting test/hello/accounts/accounts_test.exs
+* creating test/hello/accounts_test.exs
+* injecting test/hello/accounts_test.exs
 
 Add the resource to your browser scope in lib/hello_web/router.ex:
 
@@ -258,7 +258,7 @@ email:string:unique user_id:references:users
 * creating lib/hello/accounts/credential.ex
 * creating priv/repo/migrations/20170629180555_create_credentials.exs
 * injecting lib/hello/accounts.ex
-* injecting test/hello/accounts/accounts_test.exs
+* injecting test/hello/accounts_test.exs
 
 Remember to update your repository by running migrations:
 
diff --git a/guides/deployment/heroku.md b/guides/deployment/heroku.md
index 35f1a2b99..9907d9a8c 100644
--- a/guides/deployment/heroku.md
+++ b/guides/deployment/heroku.md
@@ -91,7 +91,7 @@ The URL in the output is the URL to our application. If we open it in our browse
 
 > Note: if we hadn't initialized our Git repository before we ran the `heroku create` command, we wouldn't have our Heroku remote repository properly set up at this point. We can set that up manually by running: `heroku git:remote -a [our-app-name].`
 
-The buildpack uses a predefined Elixir and Erlang version but to avoid surprises when deploying, it is best to explicitly list the Elixir and Erlang version we want in production to be the same we are using during development or in your continuous integration servers. This can be done by creating a config file named `elixir_buildpack.config` in the root directory of your project with your target version of Elixir and Erlang:
+The buildpack uses a predefined Elixir and Erlang version but to avoid surprises when deploying, it is best to explicitly list the Elixir and Erlang version we want in production to be the same we are using during development or in your continuous integration servers. This is done by creating a config file named `elixir_buildpack.config` in the root directory of your project with your target version of Elixir and Erlang:
 
 ```
 # Elixir version
diff --git a/installer/lib/mix/tasks/phx.new.ecto.ex b/installer/lib/mix/tasks/phx.new.ecto.ex
index 1a90eb1f9..77478a74d 100644
--- a/installer/lib/mix/tasks/phx.new.ecto.ex
+++ b/installer/lib/mix/tasks/phx.new.ecto.ex
@@ -56,6 +56,6 @@ defmodule Mix.Tasks.Phx.New.Ecto do
       Mix.raise "The ecto task can only be run within an umbrella's apps directory"
     end
 
-    Mix.Tasks.Phx.New.run(args ++ ["--no-webpack", "--ecto"], Phx.New.Ecto)
+    Mix.Tasks.Phx.New.run(args ++ ["--no-webpack", "--ecto"], Phx.New.Ecto, :app_path)
   end
 end
diff --git a/installer/lib/mix/tasks/phx.new.ex b/installer/lib/mix/tasks/phx.new.ex
index 4323889d4..12030f85c 100644
--- a/installer/lib/mix/tasks/phx.new.ex
+++ b/installer/lib/mix/tasks/phx.new.ex
@@ -98,48 +98,52 @@ defmodule Mix.Tasks.Phx.New do
   def run([version]) when version in ~w(-v --version) do
     Mix.shell.info("Phoenix v#{@version}")
   end
+
   def run(argv) do
     elixir_version_check!()
     case parse_opts(argv) do
-      {_opts, []}             -> Mix.Tasks.Help.run(["phx.new"])
+      {_opts, []} ->
+        Mix.Tasks.Help.run(["phx.new"])
+
       {opts, [base_path | _]} ->
         generator = if opts[:umbrella], do: Umbrella, else: Single
-        generate(base_path, generator, opts)
+        generate(base_path, generator, :project_path, opts)
     end
   end
-  def run(argv, generator) do
+
+  def run(argv, generator, path) do
     elixir_version_check!()
     case parse_opts(argv) do
-      {_opts, []}             -> Mix.Tasks.Help.run(["phx.new"])
-      {opts, [base_path | _]} -> generate(base_path, generator, opts)
+      {_opts, []} -> Mix.Tasks.Help.run(["phx.new"])
+      {opts, [base_path | _]} -> generate(base_path, generator, path, opts)
     end
   end
 
-  def generate(base_path, generator, opts) do
+  def generate(base_path, generator, path, opts) do
     base_path
     |> Project.new(opts)
     |> generator.prepare_project()
     |> Generator.put_binding()
-    |> validate_project()
+    |> validate_project(path)
     |> generator.generate()
-    |> prompt_to_install_deps(generator)
+    |> prompt_to_install_deps(generator, path)
   end
 
-  defp validate_project(%Project{opts: opts} = project) do
+  defp validate_project(%Project{opts: opts} = project, path) do
     check_app_name!(project.app, !!opts[:app])
-    check_directory_existence!(project.project_path)
+    check_directory_existence!(Map.fetch!(project, path))
     check_module_name_validity!(project.root_mod)
     check_module_name_availability!(project.root_mod)
 
     project
   end
 
-  defp prompt_to_install_deps(%Project{} = project, generator) do
+  defp prompt_to_install_deps(%Project{} = project, generator, path) do
+    path = Map.fetch!(project, path)
     install? = Mix.shell.yes?("\nFetch and install dependencies?")
+    cd_step = ["$ cd #{relative_app_path(path)}"]
 
-    cd_step = ["$ cd #{relative_app_path(project.project_path)}"]
-
-    maybe_cd(project.project_path, fn ->
+    maybe_cd(path, fn ->
       mix_step = install_mix(project, install?)
 
       compile =
@@ -158,7 +162,7 @@ defmodule Mix.Tasks.Phx.New do
       print_missing_steps(cd_step ++ mix_step ++ webpack_step)
 
       if Project.ecto?(project) do
-        print_ecto_info(project, generator)
+        print_ecto_info(generator)
       end
 
       print_mix_info(generator)
@@ -218,22 +222,16 @@ defmodule Mix.Tasks.Phx.New do
     """
   end
 
-  defp print_ecto_info(%Project{}, Web), do: :ok
-  defp print_ecto_info(%Project{app_path: nil}, _gen), do: :ok
-  defp print_ecto_info(%Project{app_path: app_path} = project, _gen) do
-    config_path =
-      app_path
-      |> Path.join("config/dev.exs")
-      |> Path.relative_to(project.project_path)
-
+  defp print_ecto_info(Web), do: :ok
+  defp print_ecto_info(_gen) do
     Mix.shell.info """
-    Then configure your database in #{config_path} and run:
+    Then configure your database in config/dev.exs and run:
 
         $ mix ecto.create
     """
   end
 
-  defp print_mix_info(gen) when gen in [Ecto] do
+  defp print_mix_info(Ecto) do
     Mix.shell.info """
     You can run your app inside IEx (Interactive Elixir) as:
 
@@ -251,10 +249,11 @@ defmodule Mix.Tasks.Phx.New do
         $ iex -S mix phx.server
     """
   end
+
   defp relative_app_path(path) do
     case Path.relative_to_cwd(path) do
       ^path -> Path.basename(path)
-      rel   -> rel
+      rel -> rel
     end
   end
 
diff --git a/installer/lib/mix/tasks/phx.new.web.ex b/installer/lib/mix/tasks/phx.new.web.ex
index dcac4e922..052c4766d 100644
--- a/installer/lib/mix/tasks/phx.new.web.ex
+++ b/installer/lib/mix/tasks/phx.new.web.ex
@@ -37,6 +37,6 @@ defmodule Mix.Tasks.Phx.New.Web do
       Mix.raise "The web task can only be run within an umbrella's apps directory"
     end
 
-    Mix.Tasks.Phx.New.run(args, Phx.New.Web)
+    Mix.Tasks.Phx.New.run(args, Phx.New.Web, :web_path)
   end
 end
diff --git a/installer/lib/phx_new/ecto.ex b/installer/lib/phx_new/ecto.ex
index 0c0ea927d..5d56a4776 100644
--- a/installer/lib/phx_new/ecto.ex
+++ b/installer/lib/phx_new/ecto.ex
@@ -6,46 +6,30 @@ defmodule Phx.New.Ecto do
   @pre "phx_umbrella/apps/app_name"
 
   template :new, [
-    {:eex,  "#{@pre}/config/config.exs",           :app, "config/config.exs"},
-    {:eex,  "#{@pre}/config/dev.exs",              :app, "config/dev.exs"},
-    {:eex,  "#{@pre}/config/prod.exs",             :app, "config/prod.exs"},
-    {:eex,  "#{@pre}/config/prod.secret.exs",      :app, "config/prod.secret.exs"},
-    {:eex,  "#{@pre}/config/test.exs",             :app, "config/test.exs"},
-    {:eex,  "#{@pre}/lib/app_name/application.ex", :app, "lib/:app/application.ex"},
-    {:eex,  "#{@pre}/lib/app_name.ex",             :app, "lib/:app.ex"},
-    {:eex,  "#{@pre}/test/test_helper.exs",        :app, "test/test_helper.exs"},
-    {:eex,  "#{@pre}/README.md",                   :app, "README.md"},
-    {:eex,  "#{@pre}/mix.exs",                     :app, "mix.exs"},
-    {:eex,  "#{@pre}/gitignore",                   :app, ".gitignore"},
-    {:eex,  "#{@pre}/formatter.exs",               :app, ".formatter.exs"},
-  ]
-
-  template :ecto, [
-    {:eex,  "phx_ecto/repo.ex",              :app, "lib/:app/repo.ex"},
-    {:keep, "phx_ecto/priv/repo/migrations", :app, "priv/repo/migrations"},
-    {:eex,  "phx_ecto/seeds.exs",            :app, "priv/repo/seeds.exs"},
-    {:eex,  "phx_ecto/data_case.ex",         :app, "test/support/data_case.ex"},
-    {:eex,  "phx_ecto/formatter.exs",        :app, "priv/repo/migrations/.formatter.exs"},
+    {:config, "#{@pre}/config/config.exs",           :project, "config/config.exs"},
+    {:eex,    "#{@pre}/lib/app_name/application.ex", :app, "lib/:app/application.ex"},
+    {:eex,    "#{@pre}/lib/app_name.ex",             :app, "lib/:app.ex"},
+    {:eex,    "#{@pre}/test/test_helper.exs",        :app, "test/test_helper.exs"},
+    {:eex,    "#{@pre}/README.md",                   :app, "README.md"},
+    {:eex,    "#{@pre}/mix.exs",                     :app, "mix.exs"},
+    {:eex,    "#{@pre}/gitignore",                   :app, ".gitignore"},
+    {:eex,    "#{@pre}/formatter.exs",               :app, ".formatter.exs"},
   ]
 
   def prepare_project(%Project{} = project) do
-    project_path = Path.expand(project.base_path)
+    app_path = Path.expand(project.base_path)
+    project_path = Path.dirname(Path.dirname(app_path))
 
     %Project{project |
              in_umbrella?: true,
-             app_path: project_path,
+             app_path: app_path,
              project_path: project_path}
   end
 
   def generate(%Project{} = project) do
     copy_from project, __MODULE__, :new
-    if Project.ecto?(project), do: gen_ecto(project)
-
+    if Project.ecto?(project), do: Phx.New.Single.gen_ecto(project)
     project
   end
 
-  defp gen_ecto(project) do
-    copy_from project, __MODULE__, :ecto
-    gen_ecto_config(project)
-  end
 end
diff --git a/installer/lib/phx_new/generator.ex b/installer/lib/phx_new/generator.ex
index 46fa1a083..765b49f7a 100644
--- a/installer/lib/phx_new/generator.ex
+++ b/installer/lib/phx_new/generator.ex
@@ -4,7 +4,6 @@ defmodule Phx.New.Generator do
   alias Phx.New.{Project}
 
   @phoenix Path.expand("../..", __DIR__)
-
   @phoenix_version Version.parse!("1.4.0")
 
   @callback prepare_project(Project.t) :: Project.t
@@ -13,8 +12,8 @@ defmodule Phx.New.Generator do
   defmacro __using__(_env) do
     quote do
       @behaviour unquote(__MODULE__)
-      import unquote(__MODULE__)
       import Mix.Generator
+      import unquote(__MODULE__)
       Module.register_attribute(__MODULE__, :templates, accumulate: true)
       @before_compile unquote(__MODULE__)
     end
@@ -22,23 +21,21 @@ defmodule Phx.New.Generator do
 
   defmacro __before_compile__(env) do
     root = Path.expand("../../templates", __DIR__)
-    templates_ast = for {name, mappings} <- Module.get_attribute(env.module, :templates) do
-      for {format, source, _, _} <- mappings, format != :keep do
-        path = Path.join(root, source)
-        quote do
-          @external_resource unquote(path)
-          def render(unquote(name), unquote(source)), do: unquote(File.read!(path))
+
+    templates_ast =
+      for {name, mappings} <- Module.get_attribute(env.module, :templates) do
+        for {format, source, _, _} <- mappings, format != :keep do
+          path = Path.join(root, source)
+          quote do
+            @external_resource unquote(path)
+            def render(unquote(name), unquote(source)), do: unquote(File.read!(path))
+          end
         end
       end
-    end
 
     quote do
       unquote(templates_ast)
       def template_files(name), do: Keyword.fetch!(@templates, name)
-      # Embed missing files from Phoenix static.
-      embed_text :phoenix_js, from_file: Path.expand("../../templates/phx_assets/phoenix.js", unquote(__DIR__))
-      embed_text :phoenix_png, from_file: Path.expand("../../templates/phx_assets/phoenix.png", unquote(__DIR__))
-      embed_text :phoenix_favicon, from_file: Path.expand("../../templates/phx_assets/favicon.ico", unquote(__DIR__))
     end
   end
 
@@ -58,8 +55,9 @@ defmodule Phx.New.Generator do
           File.mkdir_p!(target)
         :text ->
           create_file(target, mod.render(name, source))
-        :append ->
-          append_to(Path.dirname(target), Path.basename(target), mod.render(name, source))
+        :config ->
+          contents = EEx.eval_string(mod.render(name, source), project.binding, file: source)
+          config_inject(Path.dirname(target), Path.basename(target), contents)
         :eex  ->
           contents = EEx.eval_string(mod.render(name, source), project.binding, file: source)
           create_file(target, contents)
@@ -67,9 +65,29 @@ defmodule Phx.New.Generator do
     end
   end
 
-  def append_to(path, file, contents) do
+  def config_inject(path, file, to_inject) do
     file = Path.join(path, file)
-    File.write!(file, File.read!(file) <> contents)
+
+    contents =
+      case File.read(file) do
+        {:ok, bin} -> bin
+        {:error, _} -> "use Mix.Config\n"
+      end
+
+    with :error <- split_with_self(contents, "use Mix.Config\n"),
+         :error <- split_with_self(contents, "import Config\n") do
+      Mix.raise ~s[Could not find "use Mix.Config" or "import Config" in #{inspect(file)}]
+    else
+      [left, middle, right] ->
+        File.write!(file, [left, middle, ?\n, String.strip(to_inject), ?\n, right])
+    end
+  end
+
+  defp split_with_self(contents, text) do
+    case :binary.split(contents, text) do
+      [left, right] -> [left, text, right]
+      [_] -> :error
+    end
   end
 
   def in_umbrella?(app_path) do
@@ -147,24 +165,21 @@ defmodule Phx.New.Generator do
     Macro.camelize(project.app) != inspect(project.app_mod)
   end
 
-  def gen_ecto_config(%Project{app_path: app_path, binding: binding}) do
+  def gen_ecto_config(%Project{project_path: project_path, binding: binding}) do
     adapter_config = binding[:adapter_config]
 
-    append_to app_path, "config/dev.exs", """
-
+    config_inject project_path, "config/dev.exs", """
     # Configure your database
     config :#{binding[:app_name]}, #{binding[:app_module]}.Repo#{kw_to_config adapter_config[:dev]},
       pool_size: 10
     """
 
-    append_to app_path, "config/test.exs", """
-
+    config_inject project_path, "config/test.exs", """
     # Configure your database
     config :#{binding[:app_name]}, #{binding[:app_module]}.Repo#{kw_to_config adapter_config[:test]}
     """
 
-    append_to app_path, "config/prod.secret.exs", """
-
+    config_inject project_path, "config/prod.secret.exs", """
     # Configure your database
     config :#{binding[:app_name]}, #{binding[:app_module]}.Repo#{kw_to_config adapter_config[:prod]},
       pool_size: 15
diff --git a/installer/lib/phx_new/project.ex b/installer/lib/phx_new/project.ex
index 51bed3019..7ccdcb17a 100644
--- a/installer/lib/phx_new/project.ex
+++ b/installer/lib/phx_new/project.ex
@@ -55,6 +55,7 @@ defmodule Phx.New.Project do
     |> Path.join(path)
     |> expand_path_with_bindings(project)
   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, _ ->
         project |> Map.fetch!(:"#{key}") |> to_string()
diff --git a/installer/lib/phx_new/single.ex b/installer/lib/phx_new/single.ex
index 4b14b8f90..073a94e84 100644
--- a/installer/lib/phx_new/single.ex
+++ b/installer/lib/phx_new/single.ex
@@ -36,26 +36,6 @@ defmodule Phx.New.Single do
     {:eex,  "phx_gettext/errors.pot",               :project, "priv/gettext/errors.pot"}
   ]
 
-  template :ecto, [
-    {:eex,  "phx_ecto/repo.ex",              :project, "lib/:app/repo.ex"},
-    {:keep, "phx_ecto/priv/repo/migrations", :project, "priv/repo/migrations"},
-    {:eex,  "phx_ecto/formatter.exs",        :project, "priv/repo/migrations/.formatter.exs"},
-    {:eex,  "phx_ecto/seeds.exs",            :project, "priv/repo/seeds.exs"},
-    {:eex,  "phx_ecto/data_case.ex",         :project, "test/support/data_case.ex"},
-  ]
-
-  template :webpack, [
-    {:eex,  "phx_assets/webpack/webpack.config.js", :project, "assets/webpack.config.js"},
-    {:text,  "phx_assets/webpack/babelrc",          :project, "assets/.babelrc"},
-    {:text, "phx_assets/app.css",                   :project, "assets/css/app.css"},
-    {:text, "phx_assets/phoenix.css",               :project, "assets/css/phoenix.css"},
-    {:eex,  "phx_assets/webpack/app.js",            :project, "assets/js/app.js"},
-    {:eex,  "phx_assets/webpack/socket.js",         :project, "assets/js/socket.js"},
-    {:eex,  "phx_assets/webpack/package.json",      :project, "assets/package.json"},
-    {:text, "phx_assets/robots.txt",                :project, "assets/static/robots.txt"},
-    {:keep, "phx_assets/vendor",                    :project, "assets/vendor"},
-  ]
-
   template :html, [
     {:eex, "phx_web/controllers/page_controller.ex",         :project, "lib/:lib_web_name/controllers/page_controller.ex"},
     {:eex, "phx_web/templates/layout/app.html.eex",          :project, "lib/:lib_web_name/templates/layout/app.html.eex"},
@@ -67,13 +47,33 @@ defmodule Phx.New.Single do
     {:eex, "phx_test/views/page_view_test.exs",              :project, "test/:lib_web_name/views/page_view_test.exs"},
   ]
 
+  template :ecto, [
+    {:eex,  "phx_ecto/repo.ex",              :app, "lib/:app/repo.ex"},
+    {:keep, "phx_ecto/priv/repo/migrations", :app, "priv/repo/migrations"},
+    {:eex,  "phx_ecto/formatter.exs",        :app, "priv/repo/migrations/.formatter.exs"},
+    {:eex,  "phx_ecto/seeds.exs",            :app, "priv/repo/seeds.exs"},
+    {:eex,  "phx_ecto/data_case.ex",         :app, "test/support/data_case.ex"},
+  ]
+
+  template :webpack, [
+    {:eex,  "phx_assets/webpack.config.js", :web, "assets/webpack.config.js"},
+    {:text, "phx_assets/babelrc",           :web, "assets/.babelrc"},
+    {:eex,  "phx_assets/app.js",            :web, "assets/js/app.js"},
+    {:eex,  "phx_assets/socket.js",         :web, "assets/js/socket.js"},
+    {:eex,  "phx_assets/package.json",      :web, "assets/package.json"},
+    {:keep, "phx_assets/vendor",            :web, "assets/vendor"},
+  ]
+
   template :bare, []
 
   template :static, [
-    {:text, "phx_assets/app.css",     :project, "priv/static/css/app.css"},
-    {:text, "phx_assets/phoenix.css", :project, "priv/static/css/phoenix.css"},
-    {:text, "phx_assets/bare/app.js", :project, "priv/static/js/app.js"},
-    {:text, "phx_assets/robots.txt",  :project, "priv/static/robots.txt"},
+    {:text, "phx_static/app.js",      :web, "priv/static/js/app.js"},
+    {:text, "phx_static/app.css",     :web, "priv/static/css/app.css"},
+    {:text, "phx_static/phoenix.css", :web, "priv/static/css/phoenix.css"},
+    {:text, "phx_static/robots.txt",  :web, "priv/static/robots.txt"},
+    {:text, "phx_static/phoenix.js",  :web, "priv/static/js/phoenix.js"},
+    {:text, "phx_static/phoenix.png", :web, "priv/static/images/phoenix.png"},
+    {:text, "phx_static/favicon.ico", :web, "priv/static/favicon.ico"}
   ]
 
   def prepare_project(%Project{app: app} = project) when not is_nil(app) do
@@ -119,29 +119,36 @@ defmodule Phx.New.Single do
     project
   end
 
-  defp gen_html(project) do
+  def gen_html(project) do
     copy_from project, __MODULE__, :html
   end
 
-  defp gen_ecto(project) do
+  def gen_ecto(project) do
     copy_from project, __MODULE__, :ecto
     gen_ecto_config(project)
   end
 
-  defp gen_static(%Project{web_path: web_path} = project) do
+  def gen_static(%Project{} = project) do
     copy_from project, __MODULE__, :static
-    create_file Path.join(web_path, "priv/static/js/phoenix.js"), phoenix_js_text()
-    create_file Path.join(web_path, "priv/static/images/phoenix.png"), phoenix_png_text()
-    create_file Path.join(web_path, "priv/static/favicon.ico"), phoenix_favicon_text()
   end
 
-  defp gen_webpack(%Project{web_path: web_path} = project) do
+  def gen_webpack(%Project{web_path: web_path} = project) do
     copy_from project, __MODULE__, :webpack
-    create_file Path.join(web_path, "assets/static/images/phoenix.png"), phoenix_png_text()
-    create_file Path.join(web_path, "assets/static/favicon.ico"), phoenix_favicon_text()
+
+    statics = %{
+      "phx_static/app.css" => "assets/css/app.css",
+      "phx_static/phoenix.css" => "assets/css/phoenix.css",
+      "phx_static/robots.txt" => "assets/static/robots.txt",
+      "phx_static/phoenix.png" => "assets/static/images/phoenix.png",
+      "phx_static/favicon.ico" => "assets/static/favicon.ico"
+    }
+
+    for {source, target} <- statics do
+      create_file Path.join(web_path, target), render(:static, source)
+    end
   end
 
-  defp gen_bare(%Project{} = project) do
+  def gen_bare(%Project{} = project) do
     copy_from project, __MODULE__, :bare
   end
 end
diff --git a/installer/lib/phx_new/umbrella.ex b/installer/lib/phx_new/umbrella.ex
index 236129582..1c46d37dc 100644
--- a/installer/lib/phx_new/umbrella.ex
+++ b/installer/lib/phx_new/umbrella.ex
@@ -4,14 +4,15 @@ defmodule Phx.New.Umbrella do
   alias Phx.New.{Ecto, Web, Project}
 
   template :new, [
-    {:eex,  "phx_umbrella/gitignore",         :project, ".gitignore"},
-    {:eex,  "phx_umbrella/config/config.exs", :project, "config/config.exs"},
-    {:eex,  "phx_umbrella/config/dev.exs",    :project, "config/dev.exs"},
-    {:eex,  "phx_umbrella/config/test.exs",   :project, "config/test.exs"},
-    {:eex,  "phx_umbrella/config/prod.exs",   :project, "config/prod.exs"},
-    {:eex,  "phx_umbrella/mix.exs",           :project, "mix.exs"},
-    {:eex,  "phx_umbrella/README.md",         :project, "README.md"},
-    {:eex,  "phx_umbrella/formatter.exs",     :project, ".formatter.exs"},
+    {:eex,  "phx_umbrella/gitignore",              :project, ".gitignore"},
+    {:eex,  "phx_umbrella/config/config.exs",      :project, "config/config.exs"},
+    {:eex,  "phx_umbrella/config/dev.exs",         :project, "config/dev.exs"},
+    {:eex,  "phx_umbrella/config/test.exs",        :project, "config/test.exs"},
+    {:eex,  "phx_umbrella/config/prod.exs",        :project, "config/prod.exs"},
+    {:eex,  "phx_umbrella/config/prod.secret.exs", :project, "config/prod.secret.exs"},
+    {:eex,  "phx_umbrella/mix.exs",                :project, "mix.exs"},
+    {:eex,  "phx_umbrella/README.md",              :project, "README.md"},
+    {:eex,  "phx_umbrella/formatter.exs",          :project, ".formatter.exs"},
   ]
 
   def prepare_project(%Project{app: app} = project) when not is_nil(app) do
diff --git a/installer/lib/phx_new/web.ex b/installer/lib/phx_new/web.ex
index 26c8c21b2..21f32f9b4 100644
--- a/installer/lib/phx_new/web.ex
+++ b/installer/lib/phx_new/web.ex
@@ -6,11 +6,11 @@ defmodule Phx.New.Web do
   @pre "phx_umbrella/apps/app_name_web"
 
   template :new, [
-    {:eex,  "#{@pre}/config/config.exs",              :web, "config/config.exs"},
-    {:eex,  "#{@pre}/config/dev.exs",                 :web, "config/dev.exs"},
-    {:eex,  "#{@pre}/config/prod.exs",                :web, "config/prod.exs"},
-    {:eex,  "#{@pre}/config/prod.secret.exs",         :web, "config/prod.secret.exs"},
-    {:eex,  "#{@pre}/config/test.exs",                :web, "config/test.exs"},
+    {:config, "#{@pre}/config/config.exs",            :project, "config/config.exs"},
+    {:config, "#{@pre}/config/dev.exs",               :project, "config/dev.exs"},
+    {:config, "#{@pre}/config/prod.exs",              :project, "config/prod.exs"},
+    {:config, "#{@pre}/config/prod.secret.exs",       :project, "config/prod.secret.exs"},
+    {:config, "#{@pre}/config/test.exs",              :project, "config/test.exs"},
     {:eex,  "#{@pre}/lib/app_name.ex",                :web, "lib/:web_app.ex"},
     {:eex,  "#{@pre}/lib/app_name/application.ex",    :web, "lib/:web_app/application.ex"},
     {:eex,  "phx_web/channels/user_socket.ex",        :web, "lib/:web_app/channels/user_socket.ex"},
@@ -37,18 +37,6 @@ defmodule Phx.New.Web do
     {:eex,  "phx_gettext/errors.pot",               :web, "priv/gettext/errors.pot"}
   ]
 
-  template :webpack, [
-    {:eex,  "phx_assets/webpack/webpack.config.js", :web, "assets/webpack.config.js"},
-    {:text, "phx_assets/webpack/babelrc",           :web, "assets/.babelrc"},
-    {:text, "phx_assets/app.css",                   :web, "assets/css/app.css"},
-    {:text, "phx_assets/phoenix.css",               :web, "assets/css/phoenix.css"},
-    {:eex,  "phx_assets/webpack/app.js",            :web, "assets/js/app.js"},
-    {:eex,  "phx_assets/webpack/socket.js",         :web, "assets/js/socket.js"},
-    {:eex,  "phx_assets/webpack/package.json",      :web, "assets/package.json"},
-    {:text, "phx_assets/robots.txt",                :web, "assets/static/robots.txt"},
-    {:keep, "phx_assets/vendor",                    :web, "assets/vendor"},
-  ]
-
   template :html, [
     {:eex,  "phx_web/controllers/page_controller.ex",         :web, "lib/:web_app/controllers/page_controller.ex"},
     {:eex,  "phx_web/templates/layout/app.html.eex",          :web, "lib/:web_app/templates/layout/app.html.eex"},
@@ -60,22 +48,14 @@ defmodule Phx.New.Web do
     {:eex,  "phx_test/views/page_view_test.exs",              :web, "test/:web_app/views/page_view_test.exs"},
   ]
 
-  template :bare, []
-
-  template :static, [
-    {:text, "phx_assets/app.css",     :web, "priv/static/css/app.css"},
-    {:text, "phx_assets/phoenix.css", :web, "priv/static/css/phoenix.css"},
-    {:text, "phx_assets/bare/app.js", :web, "priv/static/js/app.js"},
-    {:text, "phx_assets/robots.txt",  :web, "priv/static/robots.txt"},
-  ]
-
   def prepare_project(%Project{app: app} = project) when not is_nil(app) do
-    project_path = Path.expand(project.base_path)
+    web_path = Path.expand(project.base_path)
+    project_path = Path.dirname(Path.dirname(web_path))
 
     %Project{project |
              in_umbrella?: true,
              project_path: project_path,
-             web_path: project_path,
+             web_path: web_path,
              web_app: app,
              generators: [context_app: false],
              web_namespace: project.app_mod}
@@ -88,9 +68,9 @@ defmodule Phx.New.Web do
     if Project.html?(project), do: gen_html(project)
 
     case {Project.webpack?(project), Project.html?(project)} do
-      {true, _}      -> gen_webpack(project)
-      {false, true}  -> gen_static(project)
-      {false, false} -> gen_bare(project)
+      {true, _}      -> Phx.New.Single.gen_webpack(project)
+      {false, true}  -> Phx.New.Single.gen_static(project)
+      {false, false} -> Phx.New.Single.gen_bare(project)
     end
 
     project
@@ -99,21 +79,4 @@ defmodule Phx.New.Web do
   defp gen_html(%Project{} = project) do
     copy_from project, __MODULE__, :html
   end
-
-  defp gen_static(%Project{web_path: web_path} = project) do
-    copy_from project, __MODULE__, :static
-    create_file Path.join(web_path, "priv/static/js/phoenix.js"), phoenix_js_text()
-    create_file Path.join(web_path, "priv/static/images/phoenix.png"), phoenix_png_text()
-    create_file Path.join(web_path, "priv/static/favicon.ico"), phoenix_favicon_text()
-  end
-
-  defp gen_webpack(%Project{web_path: web_path} = project) do
-    copy_from project, __MODULE__, :webpack
-    create_file Path.join(web_path, "assets/static/images/phoenix.png"), phoenix_png_text()
-    create_file Path.join(web_path, "assets/static/favicon.ico"), phoenix_favicon_text()
-  end
-
-  defp gen_bare(%Project{} = project) do
-    copy_from project, __MODULE__, :bare
-  end
 end
diff --git a/installer/templates/phx_assets/webpack/app.js b/installer/templates/phx_assets/app.js
similarity index 100%
rename from installer/templates/phx_assets/webpack/app.js
rename to installer/templates/phx_assets/app.js
diff --git a/installer/templates/phx_assets/webpack/babelrc b/installer/templates/phx_assets/babelrc
similarity index 100%
rename from installer/templates/phx_assets/webpack/babelrc
rename to installer/templates/phx_assets/babelrc
diff --git a/installer/templates/phx_assets/webpack/package.json b/installer/templates/phx_assets/package.json
similarity index 100%
rename from installer/templates/phx_assets/webpack/package.json
rename to installer/templates/phx_assets/package.json
diff --git a/installer/templates/phx_assets/webpack/socket.js b/installer/templates/phx_assets/socket.js
similarity index 100%
rename from installer/templates/phx_assets/webpack/socket.js
rename to installer/templates/phx_assets/socket.js
diff --git a/installer/templates/phx_assets/webpack/webpack.config.js b/installer/templates/phx_assets/webpack.config.js
similarity index 100%
rename from installer/templates/phx_assets/webpack/webpack.config.js
rename to installer/templates/phx_assets/webpack.config.js
diff --git a/installer/templates/phx_assets/app.css b/installer/templates/phx_static/app.css
similarity index 100%
rename from installer/templates/phx_assets/app.css
rename to installer/templates/phx_static/app.css
diff --git a/installer/templates/phx_assets/bare/app.js b/installer/templates/phx_static/app.js
similarity index 100%
rename from installer/templates/phx_assets/bare/app.js
rename to installer/templates/phx_static/app.js
diff --git a/installer/templates/phx_assets/favicon.ico b/installer/templates/phx_static/favicon.ico
similarity index 100%
rename from installer/templates/phx_assets/favicon.ico
rename to installer/templates/phx_static/favicon.ico
diff --git a/installer/templates/phx_assets/phoenix.css b/installer/templates/phx_static/phoenix.css
similarity index 100%
rename from installer/templates/phx_assets/phoenix.css
rename to installer/templates/phx_static/phoenix.css
diff --git a/installer/templates/phx_assets/phoenix.js b/installer/templates/phx_static/phoenix.js
similarity index 100%
rename from installer/templates/phx_assets/phoenix.js
rename to installer/templates/phx_static/phoenix.js
diff --git a/installer/templates/phx_assets/phoenix.png b/installer/templates/phx_static/phoenix.png
similarity index 100%
rename from installer/templates/phx_assets/phoenix.png
rename to installer/templates/phx_static/phoenix.png
diff --git a/installer/templates/phx_assets/robots.txt b/installer/templates/phx_static/robots.txt
similarity index 100%
rename from installer/templates/phx_assets/robots.txt
rename to installer/templates/phx_static/robots.txt
diff --git a/installer/templates/phx_umbrella/apps/app_name/config/config.exs b/installer/templates/phx_umbrella/apps/app_name/config/config.exs
index e88b16afd..eaaf50309 100644
--- a/installer/templates/phx_umbrella/apps/app_name/config/config.exs
+++ b/installer/templates/phx_umbrella/apps/app_name/config/config.exs
@@ -1,11 +1,4 @@
-# Since configuration is shared in umbrella projects, this file
-# should only configure the :<%= app_name %> application itself
-# and only for organization purposes. All other config goes to
-# the umbrella root.
-use Mix.Config<%= if namespaced? || ecto do %>
-
+<%= if namespaced? || ecto do %># Configure Mix tasks and generators
 config :<%= app_name %><%= if namespaced? do %>,
   namespace: <%= app_module %><% end %><%= if ecto do %>,
   ecto_repos: [<%= app_module %>.Repo]<% end %><% end %>
-
-import_config "#{Mix.env()}.exs"
diff --git a/installer/templates/phx_umbrella/apps/app_name/config/dev.exs b/installer/templates/phx_umbrella/apps/app_name/config/dev.exs
deleted file mode 100644
index 8cd2bc327..000000000
--- a/installer/templates/phx_umbrella/apps/app_name/config/dev.exs
+++ /dev/null
@@ -1,5 +0,0 @@
-# Since configuration is shared in umbrella projects, this file
-# should only configure the :<%= app_name %> application itself
-# and only for organization purposes. All other config goes to
-# the umbrella root.
-use Mix.Config
diff --git a/installer/templates/phx_umbrella/apps/app_name/config/prod.exs b/installer/templates/phx_umbrella/apps/app_name/config/prod.exs
deleted file mode 100644
index 8873b3767..000000000
--- a/installer/templates/phx_umbrella/apps/app_name/config/prod.exs
+++ /dev/null
@@ -1,7 +0,0 @@
-# Since configuration is shared in umbrella projects, this file
-# should only configure the :<%= app_name %> application itself
-# and only for organization purposes. All other config goes to
-# the umbrella root.
-use Mix.Config
-
-import_config "prod.secret.exs"
diff --git a/installer/templates/phx_umbrella/apps/app_name/config/prod.secret.exs b/installer/templates/phx_umbrella/apps/app_name/config/prod.secret.exs
deleted file mode 100644
index 874f1a539..000000000
--- a/installer/templates/phx_umbrella/apps/app_name/config/prod.secret.exs
+++ /dev/null
@@ -1,10 +0,0 @@
-use Mix.Config
-
-# In this file, we keep production configuration that
-# you'll likely want to automate and keep away from
-# your version control system.
-#
-# You should document the content of this
-# file or create a script for recreating it, since it's
-# kept out of version control and might be hard to recover
-# or recreate for your teammates (or yourself later on).
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/config/config.exs b/installer/templates/phx_umbrella/apps/app_name_web/config/config.exs
index e52c0bd93..b08e92407 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/config/config.exs
+++ b/installer/templates/phx_umbrella/apps/app_name_web/config/config.exs
@@ -1,10 +1,4 @@
-# Since configuration is shared in umbrella projects, this file
-# should only configure the :<%= web_app_name %> application itself
-# and only for organization purposes. All other config goes to
-# the umbrella root.
-use Mix.Config
-
-<%= if namespaced? || ecto || generators do %># General application configuration
+<%= if namespaced? || ecto || generators do %>
 config :<%= web_app_name %><%= if namespaced? do %>,
   namespace: <%= web_namespace %><% end %><%= if ecto do %>,
   ecto_repos: [<%= app_module %>.Repo]<% end %><%= if generators do %>,
@@ -16,7 +10,3 @@ config :<%= web_app_name %>, <%= endpoint_module %>,
   secret_key_base: "<%= secret_key_base %>",
   render_errors: [view: <%= web_namespace %>.ErrorView, accepts: ~w(<%= if html do %>html <% end %>json), layout: false],
   pubsub: [name: <%= web_namespace %>.PubSub, adapter: Phoenix.PubSub.PG2]
-
-# Import environment specific config. This must remain at the bottom
-# of this file so it overrides the configuration defined above.
-import_config "#{Mix.env()}.exs"
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 f00851b5b..f9bdc93f2 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
@@ -1,9 +1,3 @@
-# Since configuration is shared in umbrella projects, this file
-# should only configure the :<%= web_app_name %> application itself
-# and only for organization purposes. All other config goes to
-# the umbrella root.
-use Mix.Config
-
 # For development, we disable any cache and enable
 # debugging and code reloading.
 #
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 67688df68..4dc5b3366 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
@@ -1,9 +1,3 @@
-# Since configuration is shared in umbrella projects, this file
-# should only configure the :<%= web_app_name %> application itself
-# and only for organization purposes. All other config goes to
-# the umbrella root.
-use Mix.Config
-
 # For production, don't forget to configure the url host
 # to something meaningful, Phoenix uses this information
 # when generating URLs.
@@ -66,7 +60,3 @@ config :<%= web_app_name %>, <%= endpoint_module %>,
 #
 # Note you can't rely on `System.get_env/1` when using releases.
 # See the releases documentation accordingly.
-
-# Finally import the config/prod.secret.exs which should be versioned
-# separately.
-import_config "prod.secret.exs"
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/config/prod.secret.exs b/installer/templates/phx_umbrella/apps/app_name_web/config/prod.secret.exs
index 14f600146..fce3afa4b 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/config/prod.secret.exs
+++ b/installer/templates/phx_umbrella/apps/app_name_web/config/prod.secret.exs
@@ -1,12 +1,2 @@
-use Mix.Config
-
-# In this file, we keep production configuration that
-# you'll likely want to automate and keep away from
-# your version control system.
-#
-# You should document the content of this
-# file or create a script for recreating it, since it's
-# kept out of version control and might be hard to recover
-# or recreate for your teammates (or yourself later on).
 config :<%= web_app_name %>, <%= endpoint_module %>,
   secret_key_base: "<%= prod_secret_key_base %>"
diff --git a/installer/templates/phx_umbrella/config/config.exs b/installer/templates/phx_umbrella/config/config.exs
index 4a095e2db..0284598b0 100644
--- a/installer/templates/phx_umbrella/config/config.exs
+++ b/installer/templates/phx_umbrella/config/config.exs
@@ -1,17 +1,14 @@
-# This file is responsible for configuring your application
-# and its dependencies with the aid of the Mix.Config module.
+# This file is responsible for configuring your umbrella
+# and **all applications** and their dependencies with the
+# help of Mix.Config.
+#
+# Note that all applications in your umbrella share the
+# same configuration and dependencies, which is why they
+# all use the same configuration file. If you want different
+# configurations or dependencies per app, it is best to
+# move said applications out of the umbrella.
 use Mix.Config
 
-# By default, the umbrella project as well as each child
-# application will require this configuration file, as
-# configuration and dependencies are shared in an umbrella
-# project. While one could configure all applications here,
-# we prefer to keep the configuration of each individual
-# child application in their own app, but all other
-# dependencies, regardless if they belong to one or multiple
-# apps, should be configured in the umbrella to avoid confusion.
-import_config "../apps/*/config/config.exs"
-
 # Configures Elixir's Logger
 config :logger, :console,
   format: "$time $metadata[$level] $message\n",
diff --git a/installer/templates/phx_umbrella/config/prod.secret.exs b/installer/templates/phx_umbrella/config/prod.secret.exs
new file mode 100644
index 000000000..e2d45c783
--- /dev/null
+++ b/installer/templates/phx_umbrella/config/prod.secret.exs
@@ -0,0 +1,12 @@
+# In this file, we keep production configuration that
+# you'll likely want to automate and keep away from
+# your version control system.
+#
+# You should document the content of this file or create
+# a script for recreating it, since it's kept out of version
+# control and might be hard to recover or recreate for your
+# teammates (or yourself later on).
+#
+# Alternatively, you can keep this file versioned and use
+# environment variables instead.
+use Mix.Config
diff --git a/lib/mix/phoenix/context.ex b/lib/mix/phoenix/context.ex
index ffc45bd42..a80b914f3 100644
--- a/lib/mix/phoenix/context.ex
+++ b/lib/mix/phoenix/context.ex
@@ -31,7 +31,7 @@ defmodule Mix.Phoenix.Context do
     dir       = Mix.Phoenix.context_lib_path(ctx_app, basedir)
     file      = dir <> ".ex"
     test_dir  = Mix.Phoenix.context_test_path(ctx_app, basedir)
-    test_file = Path.join([test_dir, basename <> "_test.exs"])
+    test_file = test_dir <> "_test.exs"
     generate? = Keyword.get(opts, :context, true)
 
     %Context{
diff --git a/lib/mix/tasks/phx.server.ex b/lib/mix/tasks/phx.server.ex
index 1666da28e..426f5291f 100644
--- a/lib/mix/tasks/phx.server.ex
+++ b/lib/mix/tasks/phx.server.ex
@@ -6,6 +6,9 @@ defmodule Mix.Tasks.Phx.Server do
   @moduledoc """
   Starts the application by configuring all endpoints servers to run.
 
+  Note: to start the endpoint without using this mix task you must set
+  `server: true` in your `Phoenix.Endpoint` configuration.
+
   ## Command line options
 
   This task accepts the same command-line arguments as `run`.
diff --git a/lib/phoenix/controller.ex b/lib/phoenix/controller.ex
index bde5b6c58..37af085b1 100644
--- a/lib/phoenix/controller.ex
+++ b/lib/phoenix/controller.ex
@@ -894,6 +894,11 @@ defmodule Phoenix.Controller do
     * `:content_type` - the content type of the file or binary
       sent as download. It is automatically inferred from the
       filename extension
+    * `:disposition` - specifies dispositon type
+      (`:attachment` or `:inline`). If `:attachment` was used,
+      user will be prompted to save the file. If `:inline` was used,
+      the browser will attempt to open the file.
+      Defaults to `:attachment`.
     * `:charset` - the charset of the file, such as "utf-8".
       Defaults to none
     * `:offset` - the bytes to offset when reading. Defaults to `0`
@@ -945,15 +950,20 @@ defmodule Phoenix.Controller do
   defp prepare_send_download(conn, filename, opts) do
     content_type = opts[:content_type] || MIME.from_path(filename)
     encoded_filename = encode_filename(filename, Keyword.get(opts, :encode, true))
+    disposition_type = get_disposition_type(Keyword.get(opts, :disposition, :attachment))
     warn_if_ajax(conn)
     conn
     |> put_resp_content_type(content_type, opts[:charset])
-    |> put_resp_header("content-disposition", ~s[attachment; filename="#{encoded_filename}"])
+    |> put_resp_header("content-disposition", ~s[#{disposition_type}; filename="#{encoded_filename}"])
   end
 
   defp encode_filename(filename, false), do: filename
   defp encode_filename(filename, true), do: URI.encode_www_form(filename)
 
+  defp get_disposition_type(:attachment), do: "attachment"
+  defp get_disposition_type(:inline), do: "inline"
+  defp get_disposition_type(other), do: raise ArgumentError, "expected :disposition to be :attachment or :inline, got: #{inspect(other)}"
+
   defp ajax?(conn) do
     case get_req_header(conn, "x-requested-with") do
       [value] -> value in ["XMLHttpRequest", "xmlhttprequest"]
@@ -1273,24 +1283,7 @@ defmodule Phoenix.Controller do
   @doc """
   Fetches the flash storage.
   """
-  def fetch_flash(conn, _opts \\ []) do
-    session_flash = get_session(conn, "phoenix_flash")
-    conn = persist_flash(conn, session_flash || %{})
-
-    register_before_send conn, fn conn ->
-      flash = conn.private.phoenix_flash
-      flash_size = map_size(flash)
-
-      cond do
-        is_nil(session_flash) and flash_size == 0 ->
-          conn
-        flash_size > 0 and conn.status in 300..308 ->
-          put_session(conn, "phoenix_flash", flash)
-        true ->
-          delete_session(conn, "phoenix_flash")
-      end
-    end
-  end
+  defdelegate fetch_flash(conn, opts \\ []), to: Phoenix.Controller.Flash
 
   @doc """
   Persists a value in flash.
@@ -1304,9 +1297,7 @@ defmodule Phoenix.Controller do
       "Welcome Back!"
 
   """
-  def put_flash(conn, key, message) do
-    persist_flash(conn, Map.put(get_flash(conn), flash_key(key), message))
-  end
+  defdelegate put_flash(conn, key, message), to: Phoenix.Controller.Flash
 
   @doc """
   Returns a map of previously set flash messages or an empty map.
@@ -1321,10 +1312,7 @@ defmodule Phoenix.Controller do
       %{"info" => "Welcome Back!"}
 
   """
-  def get_flash(conn) do
-    Map.get(conn.private, :phoenix_flash) ||
-      raise ArgumentError, message: "flash not fetched, call fetch_flash/2"
-  end
+  defdelegate get_flash(conn), to: Phoenix.Controller.Flash
 
   @doc """
   Returns a message from flash by `key`.
@@ -1336,9 +1324,7 @@ defmodule Phoenix.Controller do
       "Welcome Back!"
 
   """
-  def get_flash(conn, key) do
-    get_flash(conn)[flash_key(key)]
-  end
+  defdelegate get_flash(conn, key), to: Phoenix.Controller.Flash
 
   @doc """
   Generates a status message from the template name.
@@ -1364,16 +1350,7 @@ defmodule Phoenix.Controller do
   @doc """
   Clears all flash messages.
   """
-  def clear_flash(conn) do
-    persist_flash(conn, %{})
-  end
-
-  defp flash_key(binary) when is_binary(binary), do: binary
-  defp flash_key(atom) when is_atom(atom), do: Atom.to_string(atom)
-
-  defp persist_flash(conn, value) do
-    put_private(conn, :phoenix_flash, value)
-  end
+  defdelegate clear_flash(conn), to: Phoenix.Controller.Flash
 
   @doc """
   Returns the current request path with its default query parameters:
diff --git a/lib/phoenix/controller/flash.ex b/lib/phoenix/controller/flash.ex
new file mode 100644
index 000000000..d7035cf93
--- /dev/null
+++ b/lib/phoenix/controller/flash.ex
@@ -0,0 +1,56 @@
+defmodule Phoenix.Controller.Flash do
+  import Plug.Conn
+
+  @moduledoc false
+
+  @session_key "phoenix_flash"
+  @session_atom :phoenix_flash
+
+  @doc false
+  def fetch_flash(conn, _opts \\ []) do
+    found_flash = get_session(conn, @session_key)
+    conn = persist_flash(conn, found_flash || %{})
+
+    register_before_send conn, fn conn ->
+      flash = Map.fetch!(conn.private, @session_atom)
+      flash_size = map_size(flash)
+
+      cond do
+        is_nil(found_flash) and flash_size == 0 ->
+          conn
+        flash_size > 0 and conn.status in 300..308 ->
+          put_session(conn, @session_key, flash)
+        true ->
+          delete_session(conn, @session_key)
+      end
+    end
+  end
+
+  @doc false
+  def clear_flash(conn) do
+    persist_flash(conn, %{})
+  end
+
+  @doc false
+  def put_flash(conn, key, message) do
+    persist_flash(conn, Map.put(get_flash(conn), flash_key(key), message))
+  end
+
+  @doc false
+  def get_flash(conn) do
+    Map.get(conn.private, @session_atom) ||
+      raise ArgumentError, "flash not fetched, call fetch_flash/2"
+  end
+
+  def get_flash(conn, key) do
+    get_flash(conn)[flash_key(key)]
+  end
+
+  defp persist_flash(conn, value) do
+    put_private(conn, @session_atom, value)
+  end
+
+  defp flash_key(binary) when is_binary(binary), do: binary
+  defp flash_key(atom) when is_atom(atom), do: Atom.to_string(atom)
+
+end
diff --git a/lib/phoenix/endpoint.ex b/lib/phoenix/endpoint.ex
index c4030ce47..07986ed59 100644
--- a/lib/phoenix/endpoint.ex
+++ b/lib/phoenix/endpoint.ex
@@ -922,6 +922,9 @@ defmodule Phoenix.Endpoint do
 
     * `:max_frame_size` - the maximum allowed frame size in bytes.
       Supported from Cowboy 2.3 onwards, defaults to "infinity"
+      
+    * `:compress` - whether to enable per message compresssion on
+      all data frames, defaults to false
 
   ## Longpoll configuration
 


Test patch:
diff --git a/installer/templates/phx_umbrella/apps/app_name/config/test.exs b/installer/templates/phx_umbrella/apps/app_name/config/test.exs
deleted file mode 100644
index 8cd2bc327..000000000
--- a/installer/templates/phx_umbrella/apps/app_name/config/test.exs
+++ /dev/null
@@ -1,5 +0,0 @@
-# Since configuration is shared in umbrella projects, this file
-# should only configure the :<%= app_name %> application itself
-# and only for organization purposes. All other config goes to
-# the umbrella root.
-use Mix.Config
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/config/test.exs b/installer/templates/phx_umbrella/apps/app_name_web/config/test.exs
index 40f0ce1d9..81892bfa1 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/config/test.exs
+++ b/installer/templates/phx_umbrella/apps/app_name_web/config/test.exs
@@ -1,9 +1,3 @@
-# Since configuration is shared in umbrella projects, this file
-# should only configure the :<%= web_app_name %> application itself
-# and only for organization purposes. All other config goes to
-# the umbrella root.
-use Mix.Config
-
 # We don't run a server during test. If one is required,
 # you can enable the server option below.
 config :<%= web_app_name %>, <%= endpoint_module %>,
diff --git a/installer/test/mix_helper.exs b/installer/test/mix_helper.exs
index ee27cf0ff..ad9a458ba 100644
--- a/installer/test/mix_helper.exs
+++ b/installer/test/mix_helper.exs
@@ -55,10 +55,15 @@ defmodule MixHelper do
 
     try do
       apps_path = Path.join(path, "apps")
+      config_path = Path.join(path, "config")
       File.rm_rf!(path)
       File.mkdir_p!(path)
       File.mkdir_p!(apps_path)
+      File.mkdir_p!(config_path)
       File.touch!(Path.join(path, "mix.exs"))
+      for file <- ~w(config.exs dev.exs test.exs prod.exs prod.secret.exs) do
+        File.write!(Path.join(config_path, file), "use Mix.Config\n")
+      end
       File.cd!(apps_path, function)
     after
       Application.put_env(:phoenix, :generators, conf_before)
diff --git a/installer/test/phx_new_umbrella_test.exs b/installer/test/phx_new_umbrella_test.exs
index 474a7383a..7fc5c667d 100644
--- a/installer/test/phx_new_umbrella_test.exs
+++ b/installer/test/phx_new_umbrella_test.exs
@@ -54,24 +54,21 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
       end
 
       assert_file root_path(@app, "config/config.exs"), fn file ->
-        assert file =~ ~S[import_config "../apps/*/config/config.exs"]
         assert file =~ ~S[import_config "#{Mix.env()}.exs"]
         assert file =~ "config :phoenix, :json_library, Jason"
-      end
-
-      assert_file app_path(@app, "config/config.exs"), fn file ->
         assert file =~ "ecto_repos: [PhxUmb.Repo]"
+        assert file =~ ":phx_umb_web, PhxUmbWeb.Endpoint"
+        assert file =~ "generators: [context_app: :phx_umb]\n"
         refute file =~ "namespace"
-        refute file =~ "config :phx_blog_web, :generators"
       end
 
-      assert_file web_path(@app, "config/config.exs"), fn file ->
-        assert file =~ "ecto_repos: [PhxUmb.Repo]"
-        assert file =~ ":phx_umb_web, PhxUmbWeb.Endpoint"
-        assert file =~ "generators: [context_app: :phx_umb]\n"
+      assert_file root_path(@app, "config/dev.exs"), fn file ->
+        assert file =~ ~r/watchers: \[\s+node:/
+        assert file =~ "lib/#{@app}_web/{live,views}/.*(ex)"
+        assert file =~ "lib/#{@app}_web/templates/.*(eex)"
       end
 
-      assert_file web_path(@app, "config/prod.exs"), fn file ->
+      assert_file root_path(@app, "config/prod.exs"), fn file ->
         assert file =~ "port: 80"
         assert file =~ ":inet6"
       end
@@ -130,11 +127,6 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
       assert_file( web_path(@app, ".gitignore"),  ~r/\n$/)
       assert_file web_path(@app, "assets/webpack.config.js"), "js/app.js"
       assert_file web_path(@app, "assets/.babelrc"), "env"
-      assert_file web_path(@app, "config/dev.exs"), fn file ->
-        assert file =~ ~r/watchers: \[\s+node:/
-        assert file =~ "lib/#{@app}_web/{live,views}/.*(ex)"
-        assert file =~ "lib/#{@app}_web/templates/.*(eex)"
-      end
       assert_file web_path(@app, "assets/static/favicon.ico")
       assert_file web_path(@app, "assets/static/images/phoenix.png")
       assert_file web_path(@app, "assets/css/app.css")
@@ -173,6 +165,10 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
 
       # Ecto
       config = ~r/config :phx_umb, PhxUmb.Repo,/
+      assert_file root_path(@app, "config/dev.exs"), config
+      assert_file root_path(@app, "config/test.exs"), config
+      assert_file root_path(@app, "config/prod.secret.exs"), config
+
       assert_file app_path(@app, "mix.exs"), fn file ->
         assert file =~ "aliases: aliases()"
         assert file =~ "ecto.setup"
@@ -180,9 +176,6 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
         assert file =~ "{:jason,"
       end
 
-      assert_file app_path(@app, "config/dev.exs"), config
-      assert_file app_path(@app, "config/test.exs"), config
-      assert_file app_path(@app, "config/prod.secret.exs"), config
       assert_file app_path(@app, "lib/#{@app}/repo.ex"), ~r"defmodule PhxUmb.Repo"
       assert_file app_path(@app, "priv/repo/seeds.exs"), ~r"PhxUmb.Repo.insert!"
       assert_file app_path(@app, "test/support/data_case.ex"), ~r"defmodule PhxUmb.DataCase"
@@ -196,7 +189,7 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
       assert msg =~ "$ cd phx_umb"
       assert msg =~ "$ mix deps.get"
 
-      assert_received {:mix_shell, :info, ["Then configure your database in apps/phx_umb/config/dev.exs" <> _]}
+      assert_received {:mix_shell, :info, ["Then configure your database in config/dev.exs" <> _]}
       assert_received {:mix_shell, :info, ["Start your Phoenix app" <> _]}
 
       # Channels
@@ -216,9 +209,12 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
       Mix.Tasks.Phx.New.run([@app, "--umbrella", "--no-html", "--no-webpack", "--no-ecto"])
 
       # No webpack
-      refute File.read!(web_path(@app, ".gitignore")) |> String.contains?("/assets/node_modules/")
-      assert_file( web_path(@app, ".gitignore"),  ~r/\n$/)
-      assert_file web_path(@app, "config/dev.exs"), ~r/watchers: \[\]/
+      assert_file web_path(@app, ".gitignore"), fn file ->
+        assert file =~ ~r/\n$/
+        refute file =~ "/assets/node_modules/"
+      end
+
+      assert_file root_path(@app, "config/dev.exs"), ~r/watchers: \[\]/
 
       # No webpack & No HTML
       refute_file web_path(@app, "priv/static/css/app.css")
@@ -234,19 +230,14 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
 
       assert_file app_path(@app, "mix.exs"), &refute(&1 =~ ~r":phoenix_ecto")
 
-      assert_file app_path(@app, "config/config.exs"), fn file ->
+      assert_file root_path(@app, "config/config.exs"), fn file ->
         refute file =~ "config :phx_blog_web, :generators"
         refute file =~ "ecto_repos:"
       end
-      assert_file web_path(@app, "config/config.exs"), fn file ->
-        refute file =~ "config :phx_blog_web, :generators"
-      end
 
-      assert_file web_path(@app, "config/dev.exs"), fn file ->
-        refute file =~ config
-      end
-      assert_file web_path(@app, "config/test.exs"), &refute(&1 =~ config)
-      assert_file web_path(@app, "config/prod.secret.exs"), &refute(&1 =~ config)
+      assert_file root_path(@app, "config/dev.exs"), &refute(&1 =~ config)
+      assert_file root_path(@app, "config/test.exs"), &refute(&1 =~ config)
+      assert_file root_path(@app, "config/prod.secret.exs"), &refute(&1 =~ config)
 
       assert_file app_path(@app, "lib/#{@app}/application.ex"), ~r/Supervisor.start_link\(/
 
@@ -292,7 +283,8 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
   test "new with binary_id" do
     in_tmp "new with binary_id", fn ->
       Mix.Tasks.Phx.New.run([@app, "--umbrella", "--binary-id"])
-      assert_file web_path(@app, "config/config.exs"), ~r/generators: \[context_app: :phx_umb, binary_id: true\]/
+      assert_file root_path(@app, "config/config.exs"),
+                  ~r/generators: \[context_app: :phx_umb, binary_id: true\]/
     end
   end
 
@@ -305,11 +297,12 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
       assert_file "phxUmb_umbrella/apps/phxUmb/mix.exs", fn file ->
         assert file =~ "app: :phxUmb"
       end
+
       assert_file "phxUmb_umbrella/apps/phxUmb_web/mix.exs", fn file ->
         assert file =~ "app: :phxUmb_web"
       end
 
-      assert_file "phxUmb_umbrella/apps/phxUmb/config/dev.exs", fn file ->
+      assert_file "phxUmb_umbrella/config/dev.exs", fn file ->
         assert file =~ ~r/config :phxUmb, PhxUmb.Repo,/
         assert file =~ "database: \"phxumb_dev\""
       end
@@ -323,13 +316,13 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
 
       assert_file "custom_path_umbrella/apps/phx_umb/mix.exs", ~r/app: :phx_umb/
       assert_file "custom_path_umbrella/apps/phx_umb_web/lib/phx_umb_web/endpoint.ex", ~r/app: :#{@app}_web/
-      assert_file "custom_path_umbrella/apps/phx_umb_web/config/config.exs", ~r/namespace: PhoteuxBlogWeb/
       assert_file "custom_path_umbrella/apps/phx_umb_web/lib/#{@app}_web.ex", ~r/use Phoenix.Controller, namespace: PhoteuxBlogWeb/
       assert_file "custom_path_umbrella/apps/phx_umb/lib/phx_umb/application.ex", ~r/defmodule PhoteuxBlog.Application/
       assert_file "custom_path_umbrella/apps/phx_umb/mix.exs", ~r/mod: {PhoteuxBlog.Application, \[\]}/
       assert_file "custom_path_umbrella/apps/phx_umb_web/lib/phx_umb_web/application.ex", ~r/defmodule PhoteuxBlogWeb.Application/
       assert_file "custom_path_umbrella/apps/phx_umb_web/mix.exs", ~r/mod: {PhoteuxBlogWeb.Application, \[\]}/
-      assert_file "custom_path_umbrella/apps/phx_umb/config/config.exs", ~r/namespace: PhoteuxBlog/
+      assert_file "custom_path_umbrella/config/config.exs", ~r/namespace: PhoteuxBlogWeb/
+      assert_file "custom_path_umbrella/config/config.exs", ~r/namespace: PhoteuxBlog/
     end
   end
 
@@ -352,11 +345,12 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
       Mix.Tasks.Phx.New.run([project_path, "--umbrella"])
 
       assert_file app_path(app, "mix.exs"), ":postgrex"
-      assert_file app_path(app, "config/dev.exs"), [~r/username: "postgres"/, ~r/password: "postgres"/, ~r/hostname: "localhost"/]
-      assert_file app_path(app, "config/test.exs"), [~r/username: "postgres"/, ~r/password: "postgres"/, ~r/hostname: "localhost"/]
-      assert_file app_path(app, "config/prod.secret.exs"), [~r/username: "postgres"/, ~r/password: "postgres"/]
       assert_file app_path(app, "lib/custom_path/repo.ex"), "Ecto.Adapters.Postgres"
 
+      assert_file root_path(app, "config/dev.exs"), [~r/username: "postgres"/, ~r/password: "postgres"/, ~r/hostname: "localhost"/]
+      assert_file root_path(app, "config/test.exs"), [~r/username: "postgres"/, ~r/password: "postgres"/, ~r/hostname: "localhost"/]
+      assert_file root_path(app, "config/prod.secret.exs"), [~r/username: "postgres"/, ~r/password: "postgres"/]
+
       assert_file web_path(app, "test/support/conn_case.ex"), "Ecto.Adapters.SQL.Sandbox.checkout"
       assert_file web_path(app, "test/support/channel_case.ex"), "Ecto.Adapters.SQL.Sandbox.checkout"
     end
@@ -369,11 +363,12 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
       Mix.Tasks.Phx.New.run([project_path, "--umbrella", "--database", "mysql"])
 
       assert_file app_path(app, "mix.exs"), ":myxql"
-      assert_file app_path(app, "config/dev.exs"), [~r/username: "root"/, ~r/password: ""/]
-      assert_file app_path(app, "config/test.exs"), [~r/username: "root"/, ~r/password: ""/]
-      assert_file app_path(app, "config/prod.secret.exs"), [~r/username: "root"/, ~r/password: ""/]
       assert_file app_path(app, "lib/custom_path/repo.ex"), "Ecto.Adapters.MyXQL"
 
+      assert_file root_path(app, "config/dev.exs"), [~r/username: "root"/, ~r/password: ""/]
+      assert_file root_path(app, "config/test.exs"), [~r/username: "root"/, ~r/password: ""/]
+      assert_file root_path(app, "config/prod.secret.exs"), [~r/username: "root"/, ~r/password: ""/]
+
       assert_file web_path(app, "test/support/conn_case.ex"), "Ecto.Adapters.SQL.Sandbox.checkout"
       assert_file web_path(app, "test/support/channel_case.ex"), "Ecto.Adapters.SQL.Sandbox.checkout"
     end
@@ -474,11 +469,11 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
           assert file =~ "lockfile: \"../../mix.lock\""
         end
 
-        assert_file "another/config/config.exs", fn file ->
+        assert_file "../config/config.exs", fn file ->
           assert file =~ "ecto_repos: [Another.Repo]"
         end
 
-        assert_file "another/config/prod.exs", fn file ->
+        assert_file "../config/prod.exs", fn file ->
           assert file =~ "port: 80"
           assert file =~ ":inet6"
         end
@@ -511,7 +506,6 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
         assert_file "another/.gitignore",  ~r/\n$/
         assert_file "another/assets/webpack.config.js", "js/app.js"
         assert_file "another/assets/.babelrc", "env"
-        assert_file "another/config/dev.exs", ~r/watchers: \[\s+node:/
         assert_file "another/assets/static/favicon.ico"
         assert_file "another/assets/static/images/phoenix.png"
         assert_file "another/assets/css/app.css"
diff --git a/installer/test/phx_new_web_test.exs b/installer/test/phx_new_web_test.exs
index a126b66c0..93ab23f5e 100644
--- a/installer/test/phx_new_web_test.exs
+++ b/installer/test/phx_new_web_test.exs
@@ -33,7 +33,7 @@ defmodule Mix.Tasks.Phx.New.WebTest do
     in_tmp_umbrella_project "new with defaults", fn ->
       Mix.Tasks.Phx.New.Web.run([@app_name])
 
-      assert_file "#{@app_name}/config/config.exs", fn file ->
+      assert_file "../config/config.exs", fn file ->
         assert file =~ "generators: [context_app: false]"
       end
 
diff --git a/test/mix/tasks/phx.gen.context_test.exs b/test/mix/tasks/phx.gen.context_test.exs
index af3920e42..b0ba398db 100644
--- a/test/mix/tasks/phx.gen.context_test.exs
+++ b/test/mix/tasks/phx.gen.context_test.exs
@@ -36,7 +36,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do
 
       assert String.ends_with?(context.dir, "lib/phoenix/blog")
       assert String.ends_with?(context.file, "lib/phoenix/blog.ex")
-      assert String.ends_with?(context.test_file, "test/phoenix/blog/blog_test.exs")
+      assert String.ends_with?(context.test_file, "test/phoenix/blog_test.exs")
       assert String.ends_with?(context.schema.file, "lib/phoenix/blog/post.ex")
     end
   end
@@ -63,7 +63,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do
 
       assert String.ends_with?(context.dir, "lib/phoenix/site/blog")
       assert String.ends_with?(context.file, "lib/phoenix/site/blog.ex")
-      assert String.ends_with?(context.test_file, "test/phoenix/site/blog/blog_test.exs")
+      assert String.ends_with?(context.test_file, "test/phoenix/site/blog_test.exs")
       assert String.ends_with?(context.schema.file, "lib/phoenix/site/blog/post.ex")
     end
   end
@@ -139,7 +139,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do
         assert file =~ "def change_post"
       end
 
-      assert_file "test/phoenix/blog/blog_test.exs", fn file ->
+      assert_file "test/phoenix/blog_test.exs", fn file ->
         assert file =~ "use Phoenix.DataCase"
         assert file =~ "describe \"posts\" do"
         assert file =~ "def post_fixture(attrs \\\\ %{})"
@@ -164,7 +164,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do
         assert file =~ "field :title, :string"
       end
 
-      assert_file "test/phoenix/blog/blog_test.exs", fn file ->
+      assert_file "test/phoenix/blog_test.exs", fn file ->
         assert file =~ "use Phoenix.DataCase"
         assert file =~ "describe \"comments\" do"
         assert file =~ "def comment_fixture(attrs \\\\ %{})"
@@ -203,7 +203,7 @@ defmodule Mix.Tasks.Phx.Gen.ContextTest do
         assert file =~ "raise \"TODO\""
       end
 
-      assert_file "test/phoenix/blog/blog_test.exs", fn file ->
+      assert_file "test/phoenix/blog_test.exs", fn file ->
         assert file =~ "use Phoenix.DataCase"
         assert file =~ "describe \"posts\" do"
         assert file =~ "def post_fixture(attrs \\\\ %{})"
diff --git a/test/mix/tasks/phx.gen.html_test.exs b/test/mix/tasks/phx.gen.html_test.exs
index 3972ef210..803dc7ca0 100644
--- a/test/mix/tasks/phx.gen.html_test.exs
+++ b/test/mix/tasks/phx.gen.html_test.exs
@@ -49,7 +49,7 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do
 
       assert_file "lib/phoenix/blog/post.ex"
       assert_file "lib/phoenix/blog.ex"
-      assert_file "test/phoenix/blog/blog_test.exs", fn file ->
+      assert_file "test/phoenix/blog_test.exs", fn file ->
         assert file =~ "alarm: ~T[15:01:01]"
         assert file =~ "alarm_usec: ~T[15:01:01.000000]"
         assert file =~ "announcement_date: ~D[2010-04-17]"
diff --git a/test/mix/tasks/phx.gen.json_test.exs b/test/mix/tasks/phx.gen.json_test.exs
index 51d274bcb..f3fe7cd89 100644
--- a/test/mix/tasks/phx.gen.json_test.exs
+++ b/test/mix/tasks/phx.gen.json_test.exs
@@ -51,7 +51,7 @@ defmodule Mix.Tasks.Phx.Gen.JsonTest do
       assert_file "lib/phoenix/blog/post.ex"
       assert_file "lib/phoenix/blog.ex"
 
-      assert_file "test/phoenix/blog/blog_test.exs", fn file ->
+      assert_file "test/phoenix/blog_test.exs", fn file ->
         assert file =~ "use Phoenix.DataCase"
       end
 
diff --git a/test/mix/tasks/phx.new_test.exs b/test/mix/tasks/phx.new_test.exs
index f8a83fc94..0876aaff1 100644
--- a/test/mix/tasks/phx.new_test.exs
+++ b/test/mix/tasks/phx.new_test.exs
@@ -95,7 +95,7 @@ defmodule Mix.Tasks.Phx.NewTest do
   test "assets are in sync with installer" do
     for file <- ~w(favicon.ico phoenix.js phoenix.png) do
       assert File.read!("priv/static/#{file}") ==
-             File.read!("installer/templates/phx_assets/#{file}")
+             File.read!("installer/templates/phx_static/#{file}")
     end
   end
 end
diff --git a/test/phoenix/controller/controller_test.exs b/test/phoenix/controller/controller_test.exs
index 1837354d5..46befb329 100644
--- a/test/phoenix/controller/controller_test.exs
+++ b/test/phoenix/controller/controller_test.exs
@@ -424,6 +424,15 @@ defmodule Phoenix.Controller.ControllerTest do
              "world"
     end
 
+    test "sends file for download with custom :disposition" do
+      conn = send_download(conn(:get, "/"), {:file, @hello_txt}, disposition: :inline)
+      assert conn.status == 200
+      assert get_resp_header(conn, "content-disposition") ==
+             ["inline; filename=\"hello.txt\""]
+      assert conn.resp_body ==
+             "world"
+    end
+
     test "sends file for download with custom :offset" do
       conn = send_download(conn(:get, "/"), {:file, @hello_txt}, offset: 2)
       assert conn.status == 200
@@ -460,6 +469,27 @@ defmodule Phoenix.Controller.ControllerTest do
       assert conn.resp_body ==
              "world"
     end
+
+    test "sends binary for download with custom :disposition" do
+      conn = send_download(conn(:get, "/"), {:binary, "world"},
+                           filename: "hello.txt", disposition: :inline)
+      assert conn.status == 200
+      assert get_resp_header(conn, "content-disposition") ==
+             ["inline; filename=\"hello.txt\""]
+      assert conn.resp_body ==
+             "world"
+    end
+
+    test "raises ArgumentError for :disposition other than :attachment or :inline" do
+      assert_raise(ArgumentError, ~r"expected :disposition to be :attachment or :inline, got: :foo", fn ->
+        send_download(conn(:get, "/"), {:file, @hello_txt}, disposition: :foo)
+      end)
+
+      assert_raise(ArgumentError, ~r"expected :disposition to be :attachment or :inline, got: :foo", fn ->
+        send_download(conn(:get, "/"), {:binary, "world"},
+                           filename: "hello.txt", disposition: :foo)
+      end)
+    end
   end
 
   describe "scrub_params/2" do
diff --git a/test/phoenix/controller/flash_test.exs b/test/phoenix/controller/flash_test.exs
index 104e306fb..c0a1761ca 100644
--- a/test/phoenix/controller/flash_test.exs
+++ b/test/phoenix/controller/flash_test.exs
@@ -11,30 +11,29 @@ defmodule Phoenix.Controller.FlashTest do
 
   test "flash is persisted when status is a redirect" do
     for status <- 300..308 do
-      conn = conn(:get, "/") |> with_session |> fetch_flash()
+      conn = conn() |> fetch_flash()
                              |> put_flash(:notice, "elixir") |> send_resp(status, "ok")
       assert get_flash(conn, :notice) == "elixir"
       assert get_resp_header(conn, "set-cookie") != []
-      conn = conn(:get, "/") |> recycle_cookies(conn) |> with_session |> fetch_flash()
+      conn = conn(recycle_cookies: conn) |> fetch_flash()
       assert get_flash(conn, :notice) == "elixir"
     end
   end
 
   test "flash is not persisted when status is not redirect" do
     for status <- [299, 309, 200, 404] do
-      conn = conn(:get, "/") |> with_session |> fetch_flash()
+      conn = conn() |> fetch_flash()
                              |> put_flash(:notice, "elixir") |> send_resp(status, "ok")
       assert get_flash(conn, :notice) == "elixir"
       assert get_resp_header(conn, "set-cookie") != []
-      conn = conn(:get, "/") |> recycle_cookies(conn) |> with_session |> fetch_flash()
+      conn = conn(recycle_cookies: conn) |> fetch_flash()
       assert get_flash(conn, :notice) == nil
     end
   end
 
   test "flash does not write to session when it is empty and no session exists" do
     conn =
-      conn(:get, "/")
-      |> with_session()
+      conn()
       |> fetch_flash()
       |> clear_flash()
       |> send_resp(302, "ok")
@@ -44,16 +43,13 @@ defmodule Phoenix.Controller.FlashTest do
 
   test "flash writes to session when it is empty and a previous session exists" do
     persisted_flash_conn =
-      conn(:get, "/")
-      |> with_session()
+      conn()
       |> fetch_flash()
       |> put_flash(:info, "existing")
       |> send_resp(302, "ok")
 
     conn =
-      conn(:get, "/")
-      |> Plug.Test.recycle_cookies(persisted_flash_conn)
-      |> with_session()
+      conn(recycle_cookies: persisted_flash_conn)
       |> fetch_flash()
       |> clear_flash()
       |> send_resp(200, "ok")
@@ -63,37 +59,36 @@ defmodule Phoenix.Controller.FlashTest do
 
   test "get_flash/1 raises ArgumentError when flash not previously fetched" do
     assert_raise ArgumentError, fn ->
-      conn(:get, "/") |> with_session |> get_flash()
+      conn() |> get_flash()
     end
   end
 
   test "get_flash/1 returns the map of messages" do
-    conn = conn(:get, "/") |> with_session |> fetch_flash([]) |> put_flash(:notice, "hi")
+    conn = conn() |> fetch_flash([]) |> put_flash(:notice, "hi")
     assert get_flash(conn) == %{"notice" => "hi"}
   end
 
   test "get_flash/2 returns the message by key" do
-    conn = conn(:get, "/") |> with_session |> fetch_flash([]) |> put_flash(:notice, "hi")
+    conn = conn() |> fetch_flash([]) |> put_flash(:notice, "hi")
     assert get_flash(conn, :notice) == "hi"
     assert get_flash(conn, "notice") == "hi"
   end
 
   test "get_flash/2 returns nil for missing key" do
-    conn = conn(:get, "/") |> with_session |> fetch_flash([])
+    conn = conn() |> fetch_flash([])
     assert get_flash(conn, :notice) == nil
     assert get_flash(conn, "notice") == nil
   end
 
   test "put_flash/3 raises ArgumentError when flash not previously fetched" do
     assert_raise ArgumentError, fn ->
-      conn(:get, "/") |> with_session |> put_flash(:error, "boom!")
+      conn() |> put_flash(:error, "boom!")
     end
   end
 
   test "put_flash/3 adds the key/message pair to the flash" do
     conn =
-      conn(:get, "/")
-      |> with_session
+      conn()
       |> fetch_flash([])
       |> put_flash(:error, "oh noes!")
       |> put_flash(:notice, "false alarm!")
@@ -106,8 +101,7 @@ defmodule Phoenix.Controller.FlashTest do
 
   test "clear_flash/1 clears the flash messages" do
     conn =
-      conn(:get, "/")
-      |> with_session
+      conn()
       |> fetch_flash([])
       |> put_flash(:error, "oh noes!")
       |> put_flash(:notice, "false alarm!")
@@ -122,4 +116,17 @@ defmodule Phoenix.Controller.FlashTest do
       conn(:get, "/") |> fetch_flash([])
     end
   end
+
+  defp conn(opts \\ []) do
+    conn = conn(:get, "/")
+
+    conn = case opts[:recycle_cookies] do
+      nil -> conn
+      old_conn -> conn |> recycle_cookies(old_conn)
+    end
+
+    conn
+    |> with_session()
+  end
+
 end