defmodule ExFactor do
@moduledoc """
`ExFactor` is a refactoring helper.
By identifying a Module, function name, and arity, it will identify all non-test usages
and extract them to a new Module.
If the Module exists, it adds the function to the end of the file and change all calls to the
new module's name.
"""
_docp = "results struct"
defstruct [:module, :path, :message, :file_contents, :state]
alias ExFactor.Changer
alias ExFactor.Extractor
alias ExFactor.Formatter
alias ExFactor.Remover
@doc """
Call Extractor, Remover, and Formatter modules
"""
def refactor(opts) do
source_module = Keyword.fetch!(opts, :source_module)
target_module = Keyword.fetch!(opts, :target_module)
dry_run = Keyword.get(opts, :dry_run, false)
opts =
opts
|> Keyword.put_new(:target_path, path(target_module))
|> Keyword.put_new(:source_path, path(source_module))
emplace = Extractor.emplace(opts)
changes = Changer.change(opts)
# remove should be last (before format)
removals = Remover.remove(opts)
format(%{additions: emplace, changes: changes, removals: removals}, dry_run)
end
def path(module) do
Path.join(["lib", Macro.underscore(module) <> ".ex"])
end
defp format(%{path: nil} = struct, _dry_run), do: struct
defp format(output, true), do: output
defp format(%{additions: adds, changes: changes, removals: removals} = output, false) do
%{
additions: format(adds),
changes: format(changes),
removals: format(removals)
}
output
end
defp format(list) when is_list(list) do
Enum.map(list, fn elem ->
format(elem)
Map.get_and_update(elem, :state, fn val -> {val, [:formatted | val]} end)
end)
end
defp format(%{state: [:unchanged]} = struct), do: struct
defp format(struct) do
Formatter.format([struct.path])
Map.get_and_update(struct, :state, fn val -> {val, [:formatted | val]} end)
end
end