Skip to content

Commit

Permalink
Add tests. Add more functions to begin to find and move target functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ckoch-cars committed Sep 8, 2021
1 parent 3eb4b67 commit 27c13dc
Show file tree
Hide file tree
Showing 10 changed files with 399 additions and 211 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -25,3 +25,5 @@ ex_factor-*.tar

# Temporary files for e.g. tests
/tmp

test/support/*_module.ex
26 changes: 20 additions & 6 deletions lib/ex_factor.ex
Expand Up @@ -3,14 +3,28 @@ defmodule ExFactor do
Documentation for `ExFactor`.
"""

alias ExFactor.Parser, as: P
alias ExFactor.Parser

def all_fns(input), do: P.all_functions(input)
def all_fns(input), do: Parser.all_functions(input)

@doc """
use `mix xref` list all the callers of a given module.
"""
# @spec callers(module()) :: list(map())
def callers(mod) do
# System.cmd("mix", ["compile"], env: [{"MIX_ENV", "test"}])
System.cmd("mix", ["xref", "callers", "#{mod}"], env: [{"MIX_ENV", "test"}])
# |> IO.inspect(label: "")
# mix xref callers mod
System.cmd("mix", ["xref", "callers", "#{mod}"], env: [{"MIX_ENV", "test"}])
|> elem(0)
|> String.trim()
|> String.split("\n")
|> mangle_list()
end

defp mangle_list(["Compiling" <> _ | tail]), do: mangle_list(tail)

defp mangle_list(list) do
Enum.map(list, fn string ->
[path, type] = String.split(string, " ")
%{filepath: path, dependency_type: type}
end)
end
end
27 changes: 27 additions & 0 deletions lib/ex_factor/evaluater.ex
@@ -0,0 +1,27 @@
defmodule ExFactor.Evaluater do
@moduledoc """
Documentation for `ExFactor.Evaluater`.
"""

alias ExFactor.Parser

def modules_to_refactor(module, func, arity) do
module
|> ExFactor.callers()
|> Enum.map(fn %{filepath: filepath} ->
filepath
|> Parser.public_functions()
|> evaluate_ast(filepath, func, arity)
end)
|> Enum.reject(&is_nil(&1))
end

defp evaluate_ast({_ast, fns}, filepath, func, arity) do
fns
|> Enum.find(fn map -> map.name == func && map.arity == arity end)
|> case do
nil -> nil
_ -> filepath
end
end
end
51 changes: 51 additions & 0 deletions lib/ex_factor/extractor.ex
@@ -0,0 +1,51 @@
defmodule ExFactor.Extractor do
@moduledoc """
Documentation for `ExFactor.Extractor`.
"""
alias ExFactor.Parser

def emplace(files, opts) do
source_path = Keyword.get(opts, :source_path)
source_module = Keyword.get(opts, :source_module)
target_module = Keyword.get(opts, :target_module)
target_path = Keyword.get(opts, :target_path)
source_function = Keyword.get(opts, :source_function)
arity = Keyword.get(opts, :arity)
target_function = Keyword.get(opts, :target_function, source_function)

Macro.underscore(source_module)
# target_path = Macro.underscore(target_module) <> ".ex"
Path.join([Mix.Project.app_path(), target_path])
# |> IO.inspect(label: "")

File.exists?(source_path) |> IO.inspect(label: "")

{_ast, functions} = Parser.public_functions(source_path)

map = Enum.find(functions, &(&1.name == source_function && &1.arity == arity))
# |> IO.inspect(label: "source function")

# map.ast
# |> Macro.to_string()
# |> IO.inspect(label: "source AST")

case File.exists?(target_path) do
true ->
"somehow we need to add the fn to this file"

_ ->
content =
quote generated: true do
defmodule unquote(target_module) do
@moduledoc false
unquote(map.ast)
end
end
|> Macro.to_string()

# |> IO.inspect(label: "quoted")

File.write(target_path, content)
end
end
end
24 changes: 23 additions & 1 deletion lib/ex_factor/parser.ex
@@ -1,11 +1,18 @@
defmodule ExFactor.Parser do
@moduledoc """
Documentation for `ExFactor`.
Documentation for `ExFactor.Parser`.
"""

@doc """
Identify public and private functions from a module AST.
"""
def all_functions(filepath) when is_binary(filepath) do
filepath
|> File.read!()
|> Code.string_to_quoted()
|> all_functions()
end

def all_functions({:ok, _ast} = input) do
{_ast, public_functions} = public_functions(input)
{ast, private_functions} = private_functions(input)
Expand All @@ -15,6 +22,14 @@ defmodule ExFactor.Parser do
@doc """
Identify public functions from a module AST.
"""
def public_functions(filepath) when is_binary(filepath) do
filepath
|> File.read!()
|> Code.string_to_quoted()
|> IO.inspect(label: "all funxs")
|> public_functions()
end

def public_functions({:ok, ast}) do
Macro.postwalk(ast, [], fn node, acc ->
{node, walk_ast(node, acc, :def)}
Expand All @@ -24,6 +39,13 @@ defmodule ExFactor.Parser do
@doc """
Identify private functions from a module AST.
"""
def private_functions(filepath) when is_binary(filepath) do
filepath
|> File.read!()
|> Code.string_to_quoted()
|> private_functions()
end

def private_functions({:ok, ast}) do
# Macro.prewalk(ast, [], fn node, acc ->
# # walk_ast(node, acc, :def)
Expand Down
11 changes: 11 additions & 0 deletions test/ex_factor/evaluater_test.exs
@@ -0,0 +1,11 @@
defmodule ExFactor.EvaluaterTest do
use ExUnit.Case
alias ExFactor.Evaluater

describe "modules_to_refactor/1" do
test "it should report callers of a module function" do
assert ["test/support/support.ex" | _] =
Evaluater.modules_to_refactor(ExFactor.Parser, :all_functions, 1)
end
end
end
75 changes: 75 additions & 0 deletions test/ex_factor/extractor_test.exs
@@ -0,0 +1,75 @@
defmodule ExFactor.ExtractorTest do
use ExUnit.Case
alias ExFactor.Extractor

setup do
File.rm("test/support/source_module.ex")
File.rm("test/support/target_module.ex")
:ok
end

test "write a new file with the function" do
content = """
defmodule ExFactorSampleModule do
@somedoc "This is somedoc"
# no aliases
def pub1(arg1) do
:ok
end
end
"""

File.write("test/support/source_module.ex", content)

# target_path = Macro.underscore(target_module)
target_path = "test/support/target_module.ex"
File.rm(target_path)

opts = [
target_path: target_path,
target_module: ExFactor.NewMod,
source_module: ExFactorSampleModule,
source_path: "test/support/source_module.ex",
source_function: :pub1,
arity: 1
]

path = Path.join([Mix.Project.app_path(), "lib", target_path <> ".ex"])
Extractor.emplace(["test/support/source_module.ex"], opts)

file = File.read!(target_path) |> IO.inspect(label: "target_path")
assert file =~ "def(pub1(arg1))"
assert file =~ "defmodule(ExFactor.NewMod) do"
end

test "write a new file with the function, infer some defaults" do
content = """
defmodule ExFactorSampleModule do
@somedoc "This is somedoc"
# no aliases
def pub1(arg1) do
:ok
end
end
"""

File.write("test/support/source_module.ex", content)
target_path = "test/support/target_module.ex"

opts = [
target_path: target_path,
target_module: ExFactor.NewMod,
source_module: ExFactorSampleModule,
source_path: "test/support/source_module.ex",
source_function: :pub1,
arity: 1
]

path = Path.join([Mix.Project.app_path(), "lib", target_path <> ".ex"])
Extractor.emplace(["test/support/source_module.ex"], opts)

file = File.read!(target_path) |> IO.inspect(label: "target_path")
assert file =~ "def(pub1(arg1))"
assert file =~ "defmodule(ExFactor.NewMod) do"
end
end

0 comments on commit 27c13dc

Please sign in to comment.