Skip to content

Commit

Permalink
Add changer module to change function refs.
Browse files Browse the repository at this point in the history
  • Loading branch information
ckoch-cars committed Sep 20, 2021
1 parent 6517e8a commit 0c4704a
Show file tree
Hide file tree
Showing 5 changed files with 404 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/ex_factor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ defmodule ExFactor do
new module's name.
"""

alias ExFactor.Changer
alias ExFactor.Extractor
alias ExFactor.Remover

Expand All @@ -26,6 +27,8 @@ defmodule ExFactor do

emplace = Extractor.emplace(opts)
remove = Remover.remove(opts)
_changes = Changer.change(opts)
|> IO.inspect(label: "")
{emplace, remove}
end

Expand Down
139 changes: 139 additions & 0 deletions lib/ex_factor/changer.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
defmodule ExFactor.Changer do
@moduledoc """
Documentation for `ExFactor.Changer`.
"""

alias ExFactor.Callers
alias ExFactor.Util

@doc """
Given all the Callers to a module, find the instances of the target function and refactor the
function module reference to the new module. Respect any existing aliases.
"""
def change(opts) do
source_module = Keyword.fetch!(opts, :source_module)
Mix.Tasks.Compile.Elixir.run([])
callers = Callers.callers(source_module)

Enum.map(callers, fn caller ->
File.read!(caller.filepath)
|> String.split("\n")
|> find_and_replace_target(opts, caller.filepath)
end)
end

defp find_and_replace_target(list, opts, filepath) do
# opts values
source_module = Keyword.fetch!(opts, :source_module)
source_function = Keyword.fetch!(opts, :source_function)
target_module = Keyword.fetch!(opts, :target_module)
_arity = Keyword.fetch!(opts, :arity)
dry_run = Keyword.get(opts, :dry_run, false)

# modified values
source_string = Util.module_to_string(source_module)
source_modules = Module.split(source_module)
source_alias = Enum.at(source_modules, -1)
target_alias = preferred_alias(list, target_module)
source_alias_alt = find_alias_as(list, source_module)

Enum.reduce(list, {:unchanged, []}, fn elem, {state, acc} ->
cond do
String.match?(elem, ~r/#{source_string}\.#{source_function}/) ->
elem = String.replace(elem, source_module, target_module)
{:changed, [elem | acc]}
String.match?(elem, ~r/#{source_alias}\.#{source_function}/) ->
elem = String.replace(elem, source_alias, target_alias)
{:changed, [elem | acc]}
String.match?(elem, ~r/#{source_alias_alt}\.#{source_function}/) ->
elem = String.replace(elem, source_alias_alt, target_alias)
{:changed, [elem | acc]}
true -> {state, [elem | acc]}
end
end)
|> maybe_add_alias(opts)
|> write_file(source_function, filepath, dry_run)
end

defp find_alias_as(list, module) do
aalias = Enum.find(list, "", fn el -> el =~ "alias #{Util.module_to_string(module)}" end)
if String.match?(aalias, ~r/, as: /) do
aalias
|> String.split("as:", trim: true)
|> Enum.at(-1)
else
""
end
end

defp preferred_alias(list, target_module) do
target_modules = Module.split(target_module)
target_alias = Enum.at(target_modules, -1)
target_alias_alt = find_alias_as(list, target_module)

if target_alias_alt == "" do
target_alias
else
target_alias_alt
end
end

defp write_file({:unchanged, contents_list}, source_function, target_path, true) do
%{
path: target_path,
state: [:unchanged],
message: "#{source_function} not found, no changes to make",
file_contents: list_to_string(contents_list)
}
end

defp write_file({state, contents_list}, _, target_path, true) do
%{
path: target_path,
state: [:dry_run, state],
message: "--dry_run changes to make",
file_contents: list_to_string(contents_list)
}
end

defp write_file({state, contents_list}, _, target_path, _dry_run) do
contents = list_to_string(contents_list)
File.write(target_path, contents, [:write])
%{
path: target_path,
state: [state],
message: "changes made",
file_contents: contents
}
end

defp list_to_string(contents_list) do
contents_list
|> Enum.reverse()
|> Enum.join("\n")
end

defp maybe_add_alias({:unchanged, contents_list}, _), do: {:unchanged, contents_list}
defp maybe_add_alias({state, contents_list}, opts) do
source_module = Keyword.fetch!(opts, :source_module)
source_string = Util.module_to_string(source_module)
target_module = Keyword.fetch!(opts, :target_module)
target_string = Util.module_to_string(target_module)

if Enum.find(contents_list, fn el -> el =~ "alias #{target_string}" end) do
{state, contents_list}
else
contents_list
|> Enum.reduce([], fn elem, acc ->
if (elem =~ "alias #{source_string}") do
new_alias = String.replace(elem, source_string, target_string)
[new_alias | [elem | acc]]
else
[elem | acc]
end
end)
|> Enum.reverse()
|> then(fn list -> {state, list} end)
end
end
end
9 changes: 9 additions & 0 deletions lib/ex_factor/util.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule ExFactor.Util do
@moduledoc false

def module_to_string(module) do
module
|> Module.split()
|> Enum.join(".")
end
end
Loading

0 comments on commit 0c4704a

Please sign in to comment.