Packages

A full featured, configurable authentication and user management system for Phoenix.

Current section

Files

Jump to
coherence lib mix tasks coh.clean.ex
Raw

lib/mix/tasks/coh.clean.ex

defmodule Mix.Tasks.Coh.Clean do
@moduledoc """
This task will clean most of the files installed by the `mix coh.install` task.
Projects created with both `coh.install` and `coherence.install` can be
cleaned with this task. It will auto detect the project structure and remove
the appropriate files.
## Examples
# Clean all the installed files
mix coh.clean --all
# Clean only the installed view and template files
mix coh.clean --views --templates
# Clean all but the models
mix coh.clean --all --no-models
# Prompt once to confirm the removal
mix coh.clean --all --confirm-once
# Clean installed options
mix coh.clean --options rememberable
mix coh.clean --options "registerable invitable trackable"
The following options are supported:
* `--all` -- clean all files
* `--views` -- clean view files
* `--templates` -- clean template files
* `--models` -- clean models files
* `--controllers` -- clean controller files
* `--email` -- clean email files
* `--web` -- clean the web/coherence_web.ex file
* `--messages` -- clean the web/coherence_messages.ex file
* `--migrations` -- clean the migration files
* `--options` -- Clean one or more specific options
* `--dry-run` -- Show what will be removed, but don't actually remove any files
* `--confirm-once` -- confirm once before removing all selected files
Disable options:
* `--no-confirm` - don't confirm before removing files
"""
use Mix.Task
import Coherence.Mix.Utils
import Mix.Ecto
alias Mix.Tasks.Coh.Install
@shortdoc "Clean files created by the coherence installer."
@dialyzer [
{:nowarn_function, raise_options_error!: 2},
]
@config_file "config/config.exs"
@remove_opts ~w(views templates models controllers emails web messages migrations config)a
@default_opts ~w(confirm)a
@clean_opts [{:options, :string}]
@switches Enum.map([:all, :confirm_once | @remove_opts] ++ @default_opts, &({&1, :boolean})) ++ @clean_opts ++ [dry_run: :boolean]
@spec run([String.t] | []) :: any
def run(args) do
args
|> OptionParser.parse(switches: @switches)
|> do_config
|> do_clean
|> do_clean_options
end
defp do_clean(config) do
confirm_once(config, fn config ->
Enum.reduce @remove_opts, config, &(remove! &2, &1)
end)
end
defp do_clean_options(config) do
confirm_once(config, fn config ->
remove! config, :options
end)
end
defp confirm_once(%{confirm_once: true} = config, fun) do
config = Map.put config, :confirm, false
if Mix.shell.yes? "Are you sure you want to delete coherence files?" do
fun.(config)
end
config
end
defp confirm_once(config, fun) do
fun.(config)
end
defp valid_option?(all_options, option) do
Enum.any? all_options, &(&1 == option)
end
defp validate_options!(options, all_options) do
case Enum.filter(options, &(not valid_option?(all_options, &1))) do
[] -> :ok
list -> raise_options_error!(list, all_options)
end
end
defp option_string(options) do
options
|> Enum.map(&Atom.to_string/1)
|> Enum.join(", ")
end
defp raise_options_error!(list, all_options) do
Mix.raise """
Invalid Option(s): '#{option_string(list)}' are not valid option(s)
Please choose from list:
'#{option_string(all_options)}'
"""
end
defp get_current_options(options) do
:coherence
|> Application.get_env(:opts)
|> log_invalid_options(options)
end
defp log_invalid_options(current_options, options) do
case Enum.filter(options, &(not valid_option?(current_options, &1))) do
[] ->
options
invalid_list ->
Mix.shell.info "Option(s) #{option_string(invalid_list)} where not found in your configuration. They will not be removed."
options -- invalid_list
end
end
defp remove_view_file!(option, config) do
view_files = Install.view_files
case view_files[option] do
nil ->
:ok
file_name ->
path = web_path ["views", "coherence", file_name]
confirm config, path, fn -> rm!(path) end
end
option
end
defp remove_template_files!(option, config) do
files = Install.template_files
files
|> Enum.find(fn {_, {opt, _}} -> opt == option end)
|> case do
nil ->
:ok
{path, _} ->
path = web_path ["templates", "coherence", "#{path}"]
confirm config, path, fn -> rm_dir!(path) end
end
option
end
defp remove_controller_files!(option, config) do
case controller_files()[option] do
nil ->
:ok
file_name ->
path = web_path ["controllers", "coherence", file_name]
confirm config, path, fn -> rm!(path) end
end
option
end
defp remove_config_options!(options, config) do
with contents when is_binary(contents) <- File.read!(@config_file),
[_, opts_string] when is_binary(opts_string) <- Regex.run(~r/opts:\s*(\[.*?\])/, contents) do
remove_config_options!(config, contents, options, opts_string)
else
error ->
Mix.raise """
Problem updating config, error: #{inspect error}
"""
end
end
defp remove_config_options!(config, contents, options, opts_string) do
new_opts_string =
Enum.reduce options, opts_string, fn option, acc ->
String.replace acc, ~r/(,\s*:#{option})|(:#{option}\s*,?\s*)/, ""
end
message = ~s'"opts: #{stringify opts_string}" with "opts: #{stringify new_opts_string}"'
confirm_config config, message, fn ->
File.write!(@config_file, String.replace(contents, opts_string, new_opts_string))
end
config
end
defp stringify(item) do
item
|> inspect
|> String.replace("\"", "")
end
defp remove_option!(config, option) do
option
|> remove_view_file!(config)
|> remove_template_files!(config)
|> remove_controller_files!(config)
end
defp remove!(%{options: options} = config, :options) do
all_options = Install.all_options
options =
options
|> String.split(" ", trim: true)
|> Enum.map(&(String.replace &1, "-", "_"))
|> Enum.map(&String.to_atom/1)
validate_options!(options, all_options)
options
|> get_current_options
|> Enum.map(&(remove_option!(config, &1)))
|> remove_config_options!(config)
config
end
defp remove!(%{templates: true} = config, :templates) do
path = web_path "templates/coherence"
confirm config, path, fn -> rm_dir!(path) end
end
defp remove!(%{views: true} = config, :views) do
path = web_path "views/coherence"
confirm config, path, fn -> rm_dir!(path) end
end
defp remove!(%{controllers: true} = config, :controllers) do
path = web_path "controllers/coherence"
confirm config, path, fn -> rm_dir!(path) end
end
defp remove!(%{models: true} = config, :models) do
path = lib_path "coherence"
confirm config, path, fn -> rm_dir!(path) end
end
defp remove!(%{web: true} = config, :web) do
path = web_path "coherence_web.ex"
confirm config, path, fn -> rm!(path) end
end
defp remove!(%{messages: true} = config, :messages) do
path = web_path "coherence_messages.ex"
confirm config, path, fn -> rm!(path) end
end
defp remove!(%{emails: true} = config, :emails) do
path = web_path ~w(emails coherence)
confirm config, path, fn -> rm_dir!(path) end
end
defp remove!(%{migrations: true} = config, :migrations) do
case Application.get_env :coherence, :repo do
nil ->
Mix.shell.error "Config.repo not configured. Skipping migration removal!"
repo ->
do_remove!(config, repo)
end
end
defp remove!(%{config: true} = config, :config) do
confirm config, "coherence config", fn ->
regex = ~r/# %% Coherence Configuration %%.+?# %% End Coherence Configuration %%/s
conf = File.read! @config_file
File.write! @config_file, String.replace(conf, regex, "")
end
end
defp remove!(config, _), do: config
defp do_remove!(config, repo) do
ensure_repo(repo, [])
path = Path.relative_to(migrations_path(repo), Mix.Project.app_path)
case Path.wildcard(path <> "/*coherence*") do
[] ->
config
files ->
confirmed? =
if config[:confirm] do
Mix.shell.info "Found migrations: " <> Enum.join(files, ", ")
Mix.shell.yes? "Delete them?"
else
true
end
if confirmed? do
Enum.each files, &(rm! &1)
end
config
end
end
defp confirm_config(%{dry_run: true} = config, message, _fun) do
Mix.shell.info "Update config " <> message
config
end
defp confirm_config(%{confirm: true} = config, message, fun) do
if Mix.shell.yes? "Update config " <> message <> "?" do
Mix.shell.info "Updating config " <> message
fun.()
end
config
end
defp confirm_config(config, message, fun) do
Mix.shell.info "Updating config " <> message
fun.()
config
end
defp confirm(%{dry_run: true} = config, path, _fun) do
Mix.shell.info "Delete #{path}"
config
end
defp confirm(%{confirm: true} = config, path, fun) do
if File.exists? path do
if Mix.shell.yes? "Delete #{path}?" do
fun.()
end
else
Mix.shell.error "Problem removing #{path}"
end
config
end
defp confirm(config, _path, fun) do
fun.()
config
end
###############
# configuration
defp do_config({opts, parsed, unknown}) do
opts
|> verify_opts(parsed, unknown)
|> Enum.into(%{})
|> do_all_config
|> do_default_config
end
defp do_all_config(%{all: true} = config) do
Enum.reduce @remove_opts, config, &(Map.put_new(&2, &1, true))
end
defp do_all_config(config), do: config
defp do_default_config(config) do
Enum.reduce @default_opts, config, fn opt, config ->
Map.put config, opt, Map.get(config, opt, true)
end
end
defp verify_opts(opts, parsed, unknown) do
verify_args!(parsed, unknown)
switch_keys = Keyword.keys @switches
case opts |> Keyword.keys |> Enum.filter(&(not &1 in switch_keys)) do
[] -> opts
list -> raise_option_errors(list)
end
end
defp coh? do
not File.exists?("web")
end
defp web_path(path) when is_binary(path), do: Path.join(web_path(), path)
defp web_path(paths), do: Path.join([web_path() | paths])
defp web_path do
if coh?(), do: Path.join("lib", otp_app() <> "_web"), else: "web"
end
defp lib_path, do: Path.join("lib", otp_app())
defp lib_path(path) when is_binary(path), do: Path.join(lib_path(), path)
defp lib_path(paths), do: Path.join([lib_path() | paths])
defp otp_app do
Mix.Project.config[:app] |> to_string
end
end