Skip to content

Commit

Permalink
Add :otp_app configuration support for extension mix tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
danschultzer committed Aug 26, 2018
1 parent f0614c7 commit c234c30
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 57 deletions.
19 changes: 17 additions & 2 deletions lib/mix/pow/extension.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@ defmodule Mix.Pow.Extension do
@moduledoc """
Utilities module for mix extension tasks.
"""
alias Pow.Config

@spec extensions(map()) :: [atom()]
def extensions(config) do
@spec extensions(map(), atom()) :: [atom()]
def extensions(config, otp_app) do
config
|> Map.get(:extension, [])
|> List.wrap()
|> Enum.map(&Module.concat(Elixir, &1))
|> maybe_fetch_otp_app_extensions(otp_app)
end

defp maybe_fetch_otp_app_extensions([], otp_app) do
Config.get([otp_app: otp_app], :extensions, [])
end
defp maybe_fetch_otp_app_extensions(extensions, _otp_app), do: extensions

@spec no_extensions_error(atom()) :: :ok
def no_extensions_error(otp_app) do
Mix.shell.error(
"""
No extensions was provided as arguments, or found in `config :#{otp_app}, :pow` configuration.
""")
end
end
29 changes: 24 additions & 5 deletions lib/mix/tasks/extension/ecto/pow.extension.ecto.gen.migrations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ defmodule Mix.Tasks.Pow.Extension.Ecto.Gen.Migrations do
@moduledoc """
Generates a migration files for extensions.
## Usage
Install migration files for explicit extensions:
mix pow.extension.ecto.gen.migrations -r MyApp.Repo --extension PowEmailConfirmation
mix pow.extension.ecto.gen.migrations -r MyApp.Repo Accounts.Organization organizations --extension PowEmailConfirmation
Use the context app configuration environment for extensions:
mix pow.extension.ecto.gen.migrations -r MyApp.Repo
mix pow.extension.ecto.gen.migrations -r MyApp.Repo Accounts.Organization organizations
Expand All @@ -24,6 +34,7 @@ defmodule Mix.Tasks.Pow.Extension.Ecto.Gen.Migrations do
|> Pow.parse_options(@switches, @default_opts)
|> parse()
|> create_migrations_files(args)
|> print_shell_instructions()
end

defp parse({config, parsed, _invalid}) do
Expand All @@ -37,17 +48,20 @@ defmodule Mix.Tasks.Pow.Extension.Ecto.Gen.Migrations do
end

defp create_migrations_files(config, args) do
context_base = Pow.context_base(Pow.context_app())
otp_app = String.to_atom(Macro.underscore(context_base))
extensions = Extension.extensions(config, otp_app)

args
|> Ecto.parse_repo()
|> Enum.map(&Ecto.ensure_repo(&1, args))
|> Enum.map(&Map.put(config, :repo, &1))
|> Enum.each(&create_extension_migration_files/1)
end
|> Enum.each(&create_extension_migration_files(&1, extensions, context_base))

defp create_extension_migration_files(config) do
extensions = Extension.extensions(config)
context_base = Pow.context_base(Pow.context_app())
%{extensions: extensions, otp_app: otp_app}
end

defp create_extension_migration_files(config, extensions, context_base) do
for extension <- extensions,
do: create_migration_files(config, extension, context_base)
end
Expand All @@ -66,4 +80,9 @@ defmodule Mix.Tasks.Pow.Extension.Ecto.Gen.Migrations do
defp empty?(%{assocs: [], attrs: [], indexes: []}),
do: true
defp empty?(_schema), do: false

defp print_shell_instructions(%{extensions: [], otp_app: otp_app}) do
Extension.no_extensions_error(otp_app)
end
defp print_shell_instructions(config), do: config
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ defmodule Mix.Tasks.Pow.Extension.Phoenix.Gen.Templates do
@moduledoc """
Generates pow extension templates for Phoenix.
mix pow.extension.phoenix.gen.templates
## Usage
Install extension templates explicitly:
mix pow.extension.phoenix.gen.templates --extension PowEmailConfirmation
Use the context app configuration environment for extensions:
mix pow.extension.phoenix.gen.templates --context-app my_app
"""
use Mix.Task

Expand All @@ -20,6 +28,7 @@ defmodule Mix.Tasks.Pow.Extension.Phoenix.Gen.Templates do
args
|> Pow.parse_options(@switches, @default_opts)
|> create_template_files()
|> print_shell_instructions()
end

@extension_templates [
Expand All @@ -28,12 +37,13 @@ defmodule Mix.Tasks.Pow.Extension.Phoenix.Gen.Templates do
]}
]
defp create_template_files({config, _parsed, _invalid}) do
structure = Phoenix.parse_structure(config)
web_module = structure[:web_module]
web_prefix = structure[:web_prefix]
extensions =
structure = Phoenix.parse_structure(config)
web_module = structure[:web_module]
web_prefix = structure[:web_prefix]
otp_app = String.to_atom(Macro.underscore(structure[:context_base]))
extensions =
config
|> Extension.extensions()
|> Extension.extensions(otp_app)
|> Enum.filter(&Keyword.has_key?(@extension_templates, &1))
|> Enum.map(&{&1, @extension_templates[&1]})

Expand All @@ -44,6 +54,11 @@ defmodule Mix.Tasks.Pow.Extension.Phoenix.Gen.Templates do
end)
end)

%{structure: structure}
%{extensions: extensions, otp_app: otp_app, structure: structure}
end

defp print_shell_instructions(%{extensions: [], otp_app: otp_app}) do
Extension.no_extensions_error(otp_app)
end
defp print_shell_instructions(config), do: config
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ defmodule Mix.Tasks.Pow.Extension.Phoenix.Mailer.Gen.Templates do
@moduledoc """
Generates Pow mailer extension templates for Phoenix.
mix pow.extension.phoenix.mailer.gen.templates
## Usage
Install extension mailer templates explicitly:
mix pow.extension.phoenix.mailer.gen.templates --extension PowEmailConfirmation
Use the context app configuration environment for extensions:
mix pow.extension.phoenix.mailer.gen.templates --context-app my_app
"""
use Mix.Task

Expand Down Expand Up @@ -32,12 +40,13 @@ defmodule Mix.Tasks.Pow.Extension.Phoenix.Mailer.Gen.Templates do
]}
]
defp create_template_files({config, _parsed, _invalid}) do
structure = Phoenix.parse_structure(config)
web_module = structure[:web_module]
web_prefix = structure[:web_prefix]
extensions =
structure = Phoenix.parse_structure(config)
web_module = structure[:web_module]
web_prefix = structure[:web_prefix]
otp_app = String.to_atom(Macro.underscore(structure[:context_base]))
extensions =
config
|> Extension.extensions()
|> Extension.extensions(otp_app)
|> Enum.filter(&Keyword.has_key?(@extension_templates, &1))
|> Enum.map(&{&1, @extension_templates[&1]})

Expand All @@ -49,9 +58,12 @@ defmodule Mix.Tasks.Pow.Extension.Phoenix.Mailer.Gen.Templates do
end)
end)

%{structure: structure}
%{extensions: extensions, otp_app: otp_app, structure: structure}
end

defp print_shell_instructions(%{extensions: [], otp_app: otp_app}) do
Extension.no_extensions_error(otp_app)
end
defp print_shell_instructions(%{structure: structure}) do
web_base = structure[:web_module]
web_prefix = structure[:web_prefix]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ defmodule Mix.Tasks.Pow.Extension.Ecto.Gen.MigrationsTest do
end)
end

test "warns if no extensions" do
File.cd!(@tmp_path, fn ->
Migrations.run(["-r", inspect(Repo)])

assert_received {:mix_shell, :error, [msg]}
assert msg =~ "No extensions was provided as arguments, or found in `config :pow, :pow` configuration."
end)
end

test "generates with :binary_id" do
options = @options ++ ~w(--binary-id)

Expand All @@ -61,6 +70,24 @@ defmodule Mix.Tasks.Pow.Extension.Ecto.Gen.MigrationsTest do
end)
end

describe "with :otp_app configuration" do
setup do
Application.put_env(:pow, :pow, extensions: [__MODULE__])
on_exit(fn ->
Application.delete_env(:pow, :pow)
end)
end

test "generates migrations" do
File.cd!(@tmp_path, fn ->
Application.put_env(:pow, :pow, extensions: [__MODULE__])
Migrations.run(["-r", inspect(Repo)])

assert [_migration_file] = File.ls!(@migrations_path)
end)
end
end

test "doesn't make duplicate migrations" do
options = @options ++ ["--extension", __MODULE__]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,27 +48,43 @@ defmodule Mix.Tasks.Pow.Extension.Phoenix.Gen.TemplatesTest do
end)
end

test "generates with :context_app" do
options = @options ++ ~w(--context-app test)

test "warns if no extensions" do
File.cd!(@tmp_path, fn ->
Templates.run(options)
Templates.run([])

for {module, expected_templates} <- @expected_template_files do
templates_path = Path.join(["lib", "test_web", "templates", Macro.underscore(module)])
dirs = templates_path |> File.ls!() |> Enum.sort()
assert_received {:mix_shell, :error, [msg]}
assert msg =~ "No extensions was provided as arguments, or found in `config :pow, :pow` configuration."
end)
end

assert dirs == Map.keys(expected_templates)
describe "with :context_app configuration" do
setup do
Application.put_env(:test, :pow, extensions: [PowResetPassword, PowEmailConfirmation])
on_exit(fn ->
Application.delete_env(:test, :pow)
end)
end

views_path = Path.join(["lib", "test_web", "views", Macro.underscore(module)])
test "generates templates" do
File.cd!(@tmp_path, fn ->
Templates.run(~w(--context-app test))

[base_name | _rest] = expected_templates |> Map.keys()
view_content = views_path |> Path.join(base_name <> "_view.ex") |> File.read!()
for {module, expected_templates} <- @expected_template_files do
templates_path = Path.join(["lib", "test_web", "templates", Macro.underscore(module)])
dirs = templates_path |> File.ls!() |> Enum.sort()

assert view_content =~ "defmodule TestWeb.#{inspect(module)}.#{Macro.camelize(base_name)}View do"
assert view_content =~ "use TestWeb, :view"
end
end)
assert dirs == Map.keys(expected_templates)

views_path = Path.join(["lib", "test_web", "views", Macro.underscore(module)])

[base_name | _rest] = expected_templates |> Map.keys()
view_content = views_path |> Path.join(base_name <> "_view.ex") |> File.read!()

assert view_content =~ "defmodule TestWeb.#{inspect(module)}.#{Macro.camelize(base_name)}View do"
assert view_content =~ "use TestWeb, :view"
end
end)
end
end

defp ls(path), do: path |> File.ls!() |> Enum.sort()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,33 +60,49 @@ defmodule Mix.Tasks.Pow.Extension.Phoenix.Mailer.Gen.TemplatesTest do
end)
end

test "generates with :context_app" do
options = @options ++ ~w(--context-app test)

test "warns if no extensions" do
File.cd!(@tmp_path, fn ->
Templates.run(options)
Templates.run([])

for {module, expected_templates} <- @expected_template_files do
templates_path = Path.join(["lib", "test_web", "templates", Macro.underscore(module)])
dirs = templates_path |> File.ls!() |> Enum.sort()
assert_received {:mix_shell, :error, [msg]}
assert msg =~ "No extensions was provided as arguments, or found in `config :pow, :pow` configuration."
end)
end

assert dirs == Map.keys(expected_templates)
describe "with :context_app configuration" do
setup do
Application.put_env(:test, :pow, extensions: [PowResetPassword, PowEmailConfirmation])
on_exit(fn ->
Application.delete_env(:test, :pow)
end)
end

views_path = Path.join(["lib", "test_web", "views", Macro.underscore(module)])
[base_name | _rest] = expected_templates |> Map.keys()
view_content = views_path |> Path.join(base_name <> "_view.ex") |> File.read!()
test "generates mailer templates" do
File.cd!(@tmp_path, fn ->
Templates.run(~w(--context-app test))

assert view_content =~ "defmodule TestWeb.#{inspect(module)}.#{Macro.camelize(base_name)}View do"
assert view_content =~ "use TestWeb, :mailer_view"
end
for {module, expected_templates} <- @expected_template_files do
templates_path = Path.join(["lib", "test_web", "templates", Macro.underscore(module)])
dirs = templates_path |> File.ls!() |> Enum.sort()

for _ <- 1..6, do: assert_received({:mix_shell, :info, [_msg]})
assert_received {:mix_shell, :info, [msg]}
assert msg =~ "lib/test_web.ex"
assert msg =~ ":mailer_view"
assert msg =~ "def mailer_view"
assert msg =~ "use Phoenix.View, root: \"lib/test_web/templates\""
end)
assert dirs == Map.keys(expected_templates)

views_path = Path.join(["lib", "test_web", "views", Macro.underscore(module)])
[base_name | _rest] = expected_templates |> Map.keys()
view_content = views_path |> Path.join(base_name <> "_view.ex") |> File.read!()

assert view_content =~ "defmodule TestWeb.#{inspect(module)}.#{Macro.camelize(base_name)}View do"
assert view_content =~ "use TestWeb, :mailer_view"
end

for _ <- 1..6, do: assert_received({:mix_shell, :info, [_msg]})
assert_received {:mix_shell, :info, [msg]}
assert msg =~ "lib/test_web.ex"
assert msg =~ ":mailer_view"
assert msg =~ "def mailer_view"
assert msg =~ "use Phoenix.View, root: \"lib/test_web/templates\""
end)
end
end

defp ls(path), do: path |> File.ls!() |> Enum.sort()
Expand Down

0 comments on commit c234c30

Please sign in to comment.