Current section
Files
Jump to
Current section
Files
lib/ex_stub_utils.ex
defmodule ExStub.Utils do
@moduledoc """
ExStub utilitie class.
This module is for internal use only.
"""
@doc """
Gets the functions passed to the stub
"""
def functions_passed({:__block__, _, functions}), do: functions
def functions_passed(nil), do: []
def functions_passed(functions), do: [functions]
@doc """
Convert the list of defs to DefInfo
"""
def functions_and_params(functions) do
Enum.map(functions, &DefInfo.parse/1)
end
@doc """
Get the functions that done exisit in the stub module
"""
def non_exisiting_functions(module_functions, stub_functions) do
module_functions
|> Enum.filter(fn func -> !stub_funcs_contains_func(stub_functions, func) end)
end
@doc """
Get the functions that dont have a catch all in the stub module
"""
def catch_all_functions(module_functions, stub_functions) do
catch_all_funcs = Enum.filter(stub_functions, fn func -> func.catches_all == true end)
module_functions
|> Enum.filter(fn func -> stub_funcs_contains_func(stub_functions, func) end)
|> Enum.filter(fn func -> !stub_funcs_contains_func(catch_all_funcs, func) end)
end
defp stub_funcs_contains_func(stub_functions, {name, arity}) do
Enum.any?(stub_functions, fn stub_function ->
stub_function.name == name && stub_function.arity == arity
end)
end
@doc """
Check that all the stub functions exist in the original module
"""
def check_all_functions_exist(stub_functions, module_functions, module) do
stub_functions = Enum.map(stub_functions, &DefInfo.name_arity/1)
Enum.map(stub_functions, fn func ->
is_member = Enum.member?(module_functions, func)
throw_exception_if_needed(is_member, func, module)
end)
end
defp throw_exception_if_needed(false, func, module) do
desc = """
Cannot provide implementations for methods that are not in the original module
The def `#{inspect(func)}` is not defined in module `#{inspect(module)}`
"""
raise desc
end
defp throw_exception_if_needed(_, _, _), do: true
##########################################################################################
# Catch all
##########################################################################################
def stub_functions_with_catch_all(stub_functions, catch_all_to_add, module) do
stub_functions_with_catch_all(Enum.reverse(stub_functions), [], catch_all_to_add, module)
end
# Last
defp stub_functions_with_catch_all([], acc, _, _) do
acc |> Enum.reverse
end
# First
defp stub_functions_with_catch_all([h| tail], acc, to_add, module) do
info = DefInfo.parse(h)
name_arity = DefInfo.name_arity(info)
updated_list = update_funcst_to_add(to_add, name_arity)
{functions, remaining_to_add} = create_method_and_append_it(h, module, updated_list)
stub_functions_with_catch_all(tail,
acc ++ functions,
remaining_to_add,
module
)
end
defp create_method_and_append_it(current, module, {:ok, to_add}) do
{:ok, created} = ExStub.Generator.create_method(current, module) |> Code.string_to_quoted
{[created, current], to_add}
end
defp create_method_and_append_it(current, _, {:not_found, to_add}) do
{[current], to_add}
end
defp update_funcst_to_add(to_add, func) do
if Enum.member?(to_add, func) do
{:ok, List.delete(to_add, func)}
else
{:not_found, to_add}
end
end
##########################################################################################
# Add Recording to default
##########################################################################################
def add_recording_to_default(funcs) do
Enum.map(funcs, fn func -> add_recording_to_func(func) end)
end
defp add_recording_to_func(func) do
params = name_params_for_func(func)
body = add_recording_to_body(func)
func
|> replace_params_in_func(params)
|> replace_body_in_func(body)
end
defp name_params_for_func({_, _, [{_, _, nil}, _]}), do: []
defp name_params_for_func({_, _, [{_, _, params}, _]}) do
Enum.reduce(params, {1, []}, fn (param, {index, acc}) ->
new_param = name_param(param, index)
{index + 1, acc ++ [new_param]}
end)
|> elem(1)
end
defp replace_params_in_func({:def, line, [{func_name, func_line, _}, body]}, new_params) do
{:def, line, [{func_name, func_line, new_params}, body]}
end
defp replace_body_in_func({:def, line, [func_def, _]}, new_body) do
{:def, line, [func_def, new_body]}
end
defp name_param({_, line, _}=param, index) do
{:=, [], [param, {:"p#{index}__", line, nil}]}
end
defp name_param(param, index) do
{:=, [], [param, {:"p#{index}__", [], nil}]}
end
defp add_recording_to_body({_, _, [{func_name, _, params}, [do: body]]}) do
call = ExStub.Generator.recording_call(func_name, params_count(params))
[do: add_call_to_body(call, body)]
end
[do: {:__block__, [line: 20],
[{{:., [line: 23],
[{:__aliases__, [counter: 0, line: 23], [:MyStubRecording]},
:__record_call__]}, [line: 23], [:process, [{:p1, [line: 23], nil}]]},
:new2]}]
def add_call_to_body(call, {:__block__, _, body}) do
{:__block__, [], [call| body]}
end
def add_call_to_body(call, body) do
{:__block__, [], [call, body]}
end
def params_count(nil), do: 0
def params_count(params), do: params |> Enum.count
end