mirror of
https://github.com/ash-project/igniter.git
synced 2024-09-19 13:02:51 +12:00
write a bunch more code, and add some tests, rework various patterns
This commit is contained in:
parent
b5354ce0e3
commit
281ec0f656
16 changed files with 841 additions and 276 deletions
|
@ -9,3 +9,4 @@
|
||||||
# TODO list:
|
# TODO list:
|
||||||
|
|
||||||
- [ ] properly parse args, not `"--dry-run" in argv`. Do we want to have some kind of "maybe parsed" args structure so we can call tasks with `argv` or one of those? Maybe.
|
- [ ] properly parse args, not `"--dry-run" in argv`. Do we want to have some kind of "maybe parsed" args structure so we can call tasks with `argv` or one of those? Maybe.
|
||||||
|
- [ ] the mix deps.get step is slow and weird sometimes, need to figure out why.
|
||||||
|
|
19
lib/args.ex
19
lib/args.ex
|
@ -1,4 +1,23 @@
|
||||||
defmodule Igniter.Args do
|
defmodule Igniter.Args do
|
||||||
|
def validate_nth_present_and_underscored(igniter, argv, n, option, message) do
|
||||||
|
value = Enum.at(argv, n)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
!value ->
|
||||||
|
{:error, Igniter.add_issue(igniter, message)}
|
||||||
|
|
||||||
|
not (Macro.underscore(value) == value) ->
|
||||||
|
{:error,
|
||||||
|
Igniter.add_issue(
|
||||||
|
igniter,
|
||||||
|
"Must provide the #{option} in snake_case. Did you mean `#{Macro.underscore(value)}`"
|
||||||
|
)}
|
||||||
|
|
||||||
|
true ->
|
||||||
|
{:ok, value}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def validate_present_and_underscored(igniter, opts, option, message) do
|
def validate_present_and_underscored(igniter, opts, option, message) do
|
||||||
cond do
|
cond do
|
||||||
!opts[option] ->
|
!opts[option] ->
|
||||||
|
|
353
lib/common.ex
353
lib/common.ex
|
@ -1,4 +1,7 @@
|
||||||
defmodule Igniter.Common do
|
defmodule Igniter.Common do
|
||||||
|
@doc """
|
||||||
|
Common utilities for working with igniter, primarily with zippers.
|
||||||
|
"""
|
||||||
alias Sourceror.Zipper
|
alias Sourceror.Zipper
|
||||||
|
|
||||||
def find(zipper, direction \\ :next, pred) do
|
def find(zipper, direction \\ :next, pred) do
|
||||||
|
@ -16,7 +19,7 @@ defmodule Igniter.Common do
|
||||||
quote do
|
quote do
|
||||||
ast =
|
ast =
|
||||||
unquote(zipper)
|
unquote(zipper)
|
||||||
|> Igniter.Common.maybe_enter_block()
|
|> Igniter.Common.maybe_move_to_block()
|
||||||
|> Zipper.subtree()
|
|> Zipper.subtree()
|
||||||
|> Zipper.root()
|
|> Zipper.root()
|
||||||
|
|
||||||
|
@ -24,15 +27,18 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defmacro find_pattern(zipper, direction \\ :next, pattern) do
|
defmacro move_to_pattern(zipper, direction \\ :next, pattern) do
|
||||||
quote do
|
quote do
|
||||||
Sourceror.Zipper.find(unquote(zipper), unquote(direction), fn
|
case Sourceror.Zipper.find(unquote(zipper), unquote(direction), fn
|
||||||
unquote(pattern) ->
|
unquote(pattern) ->
|
||||||
true
|
true
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
false
|
false
|
||||||
end)
|
end) do
|
||||||
|
nil -> :error
|
||||||
|
value -> {:ok, value}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -56,16 +62,21 @@ defmodule Igniter.Common do
|
||||||
zipper
|
zipper
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_code(zipper, new_code) when is_binary(new_code) do
|
||||||
|
code = Sourceror.parse_string!(new_code)
|
||||||
|
|
||||||
|
add_code(zipper, code)
|
||||||
|
end
|
||||||
|
|
||||||
def add_code(zipper, new_code) do
|
def add_code(zipper, new_code) do
|
||||||
current_code =
|
current_code =
|
||||||
zipper
|
zipper
|
||||||
|> Zipper.subtree()
|
|> Zipper.subtree()
|
||||||
|> Zipper.root()
|
|> Zipper.root()
|
||||||
|> IO.inspect()
|
|
||||||
|
|
||||||
case current_code do
|
case current_code do
|
||||||
{:__block__, block_meta, stuff} ->
|
{:__block__, _, stuff} ->
|
||||||
Zipper.replace(zipper, {:__block__, block_meta, stuff ++ [new_code]})
|
Zipper.replace(zipper, {:__block__, [], stuff ++ [new_code]})
|
||||||
|
|
||||||
code ->
|
code ->
|
||||||
Zipper.replace(zipper, {:__block__, [], [code, new_code]})
|
Zipper.replace(zipper, {:__block__, [], [code, new_code]})
|
||||||
|
@ -84,26 +95,27 @@ defmodule Igniter.Common do
|
||||||
|
|
||||||
defp do_put_in_keyword(zipper, [key | rest], value, updater) do
|
defp do_put_in_keyword(zipper, [key | rest], value, updater) do
|
||||||
if node_matches_pattern?(zipper, value when is_list(value)) do
|
if node_matches_pattern?(zipper, value when is_list(value)) do
|
||||||
case find_list_item(zipper, fn item ->
|
case move_to_list_item(zipper, fn item ->
|
||||||
if is_tuple?(item) do
|
if is_tuple?(item) do
|
||||||
first_elem = tuple_elem(item, 0)
|
first_elem = tuple_elem(item, 0)
|
||||||
first_elem && node_matches_pattern?(first_elem, ^key)
|
first_elem && node_matches_pattern?(first_elem, ^key)
|
||||||
end
|
end
|
||||||
end) do
|
end) do
|
||||||
nil ->
|
:error ->
|
||||||
value = keywordify(rest, value)
|
value = keywordify(rest, value)
|
||||||
|
|
||||||
prepend_to_list(
|
{:ok,
|
||||||
zipper,
|
prepend_to_list(
|
||||||
{{:__block__, [format: :keyword], [key]}, {:__block__, [], [value]}}
|
zipper,
|
||||||
)
|
{{:__block__, [format: :keyword], [key]}, {:__block__, [], [value]}}
|
||||||
|
)}
|
||||||
|
|
||||||
zipper ->
|
{:ok, zipper} ->
|
||||||
zipper
|
zipper
|
||||||
|> tuple_elem(1)
|
|> tuple_elem(1)
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
do_put_in_keyword(zipper, rest, value, updater)
|
do_put_in_keyword(zipper, rest, value, updater)
|
||||||
|
@ -114,46 +126,171 @@ defmodule Igniter.Common do
|
||||||
|
|
||||||
def set_keyword_key(zipper, key, value, updater) do
|
def set_keyword_key(zipper, key, value, updater) do
|
||||||
if node_matches_pattern?(zipper, value when is_list(value)) do
|
if node_matches_pattern?(zipper, value when is_list(value)) do
|
||||||
case find_list_item(zipper, fn item ->
|
case move_to_list_item(zipper, fn item ->
|
||||||
if is_tuple?(item) do
|
if is_tuple?(item) do
|
||||||
first_elem = tuple_elem(item, 0)
|
first_elem = tuple_elem(item, 0)
|
||||||
first_elem && node_matches_pattern?(first_elem, ^key)
|
first_elem && node_matches_pattern?(first_elem, ^key)
|
||||||
end
|
end
|
||||||
end) do
|
end) do
|
||||||
nil ->
|
:error ->
|
||||||
prepend_to_list(
|
{:ok,
|
||||||
zipper,
|
prepend_to_list(
|
||||||
{{:__block__, [format: :keyword], [key]}, {:__block__, [], [value]}}
|
zipper,
|
||||||
)
|
{{:__block__, [format: :keyword], [key]}, {:__block__, [], [value]}}
|
||||||
|
)}
|
||||||
|
|
||||||
zipper ->
|
{:ok, zipper} ->
|
||||||
zipper
|
zipper
|
||||||
|> tuple_elem(1)
|
|> tuple_elem(1)
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
updater.(zipper)
|
{:ok, updater.(zipper)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_function_call_in_current_scope(zipper, name, arity, predicate \\ fn _ -> true end) do
|
def put_in_map(zipper, path, value, updater \\ nil) do
|
||||||
case Zipper.down(zipper) do
|
updater = updater || fn _ -> value end
|
||||||
nil ->
|
|
||||||
nil
|
|
||||||
|
|
||||||
zipper ->
|
do_put_in_map(zipper, path, value, updater)
|
||||||
find_right(zipper, fn zipper ->
|
end
|
||||||
is_function_call(zipper, name, arity) && predicate.(zipper)
|
|
||||||
|
defp do_put_in_map(zipper, [key], value, updater) do
|
||||||
|
set_map_key(zipper, key, value, updater)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_put_in_map(zipper, [key | rest], value, updater) do
|
||||||
|
cond do
|
||||||
|
node_matches_pattern?(zipper, {:%{}, _, []}) ->
|
||||||
|
{:ok,
|
||||||
|
Zipper.append_child(
|
||||||
|
zipper,
|
||||||
|
mappify([key | rest], value)
|
||||||
|
)}
|
||||||
|
|
||||||
|
node_matches_pattern?(zipper, {:%{}, _, _}) ->
|
||||||
|
zipper
|
||||||
|
|> Zipper.down()
|
||||||
|
|> move_to_list_item(fn item ->
|
||||||
|
if is_tuple?(item) do
|
||||||
|
first_elem = tuple_elem(item, 0)
|
||||||
|
first_elem && node_matches_pattern?(first_elem, ^key)
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|> case do
|
||||||
|
:error ->
|
||||||
|
format = map_keys_format(zipper)
|
||||||
|
value = mappify(rest, value)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
prepend_to_list(
|
||||||
|
zipper,
|
||||||
|
{{:__block__, [format: format], [key]}, {:__block__, [], [value]}}
|
||||||
|
)}
|
||||||
|
|
||||||
|
{:ok, zipper} ->
|
||||||
|
zipper
|
||||||
|
|> tuple_elem(1)
|
||||||
|
|> case do
|
||||||
|
nil ->
|
||||||
|
:error
|
||||||
|
|
||||||
|
zipper ->
|
||||||
|
do_put_in_map(zipper, rest, value, updater)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
true ->
|
||||||
|
:error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_map_key(zipper, key, value, updater) do
|
||||||
|
cond do
|
||||||
|
node_matches_pattern?(zipper, {:%{}, _, []}) ->
|
||||||
|
{:ok,
|
||||||
|
Zipper.append_child(
|
||||||
|
zipper,
|
||||||
|
mappify([key], value)
|
||||||
|
)}
|
||||||
|
|
||||||
|
node_matches_pattern?(zipper, {:%{}, _, _}) ->
|
||||||
|
zipper
|
||||||
|
|> Zipper.down()
|
||||||
|
|> move_to_list_item(fn item ->
|
||||||
|
if is_tuple?(item) do
|
||||||
|
first_elem = tuple_elem(item, 0)
|
||||||
|
first_elem && node_matches_pattern?(first_elem, ^key)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
:error ->
|
||||||
|
format = map_keys_format(zipper)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
prepend_to_list(
|
||||||
|
zipper,
|
||||||
|
{{:__block__, [format: format], [key]}, {:__block__, [], [value]}}
|
||||||
|
)}
|
||||||
|
|
||||||
|
{:ok, zipper} ->
|
||||||
|
zipper
|
||||||
|
|> tuple_elem(1)
|
||||||
|
|> case do
|
||||||
|
nil ->
|
||||||
|
:error
|
||||||
|
|
||||||
|
zipper ->
|
||||||
|
{:ok, updater.(zipper)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
true ->
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp map_keys_format(zipper) do
|
||||||
|
zipper
|
||||||
|
|> Zipper.subtree()
|
||||||
|
|> Zipper.node()
|
||||||
|
|> case do
|
||||||
|
value when is_list(value) ->
|
||||||
|
Enum.all?(value, fn
|
||||||
|
{:__block__, meta, _} ->
|
||||||
|
meta[:format] == :keyword
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
true ->
|
||||||
|
:keyword
|
||||||
|
|
||||||
|
false ->
|
||||||
|
:map
|
||||||
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
:map
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def move_to_function_call_in_current_scope(zipper, name, arity, predicate \\ fn _ -> true end) do
|
||||||
|
zipper
|
||||||
|
|> maybe_move_to_block()
|
||||||
|
|> move_right(fn zipper ->
|
||||||
|
is_function_call(zipper, name, arity) && predicate.(zipper)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
def is_function_call(zipper, name, arity) do
|
def is_function_call(zipper, name, arity) do
|
||||||
zipper
|
zipper
|
||||||
|
|> maybe_move_to_block()
|
||||||
|> Zipper.subtree()
|
|> Zipper.subtree()
|
||||||
|> Zipper.root()
|
|> Zipper.root()
|
||||||
|> case do
|
|> case do
|
||||||
|
@ -181,7 +318,7 @@ defmodule Igniter.Common do
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
func.(zipper)
|
func.(zipper)
|
||||||
|
@ -191,7 +328,7 @@ defmodule Igniter.Common do
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
zipper
|
zipper
|
||||||
|
@ -199,17 +336,17 @@ defmodule Igniter.Common do
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
zipper
|
zipper
|
||||||
|> nth_right(index)
|
|> nth_right(index)
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
:error ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
nth ->
|
{:ok, nth} ->
|
||||||
func.(nth)
|
{:ok, func.(nth)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -225,11 +362,11 @@ defmodule Igniter.Common do
|
||||||
zipper
|
zipper
|
||||||
|> nth_right(index)
|
|> nth_right(index)
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
:error ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
nth ->
|
{:ok, nth} ->
|
||||||
func.(nth)
|
{:ok, func.(nth)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -262,10 +399,17 @@ defmodule Igniter.Common do
|
||||||
zipper ->
|
zipper ->
|
||||||
zipper
|
zipper
|
||||||
|> nth_right(index - 1)
|
|> nth_right(index - 1)
|
||||||
|> maybe_enter_block()
|
|> case do
|
||||||
|> Zipper.subtree()
|
:error ->
|
||||||
|> Zipper.root()
|
false
|
||||||
|> func.()
|
|
||||||
|
{:ok, zipper} ->
|
||||||
|
zipper
|
||||||
|
|> maybe_move_to_block()
|
||||||
|
|> Zipper.subtree()
|
||||||
|
|> Zipper.root()
|
||||||
|
|> func.()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -279,10 +423,17 @@ defmodule Igniter.Common do
|
||||||
zipper ->
|
zipper ->
|
||||||
zipper
|
zipper
|
||||||
|> nth_right(index)
|
|> nth_right(index)
|
||||||
|> maybe_enter_block()
|
|> case do
|
||||||
|> Zipper.subtree()
|
:error ->
|
||||||
|> Zipper.root()
|
false
|
||||||
|> func.()
|
|
||||||
|
{:ok, zipper} ->
|
||||||
|
zipper
|
||||||
|
|> maybe_move_to_block()
|
||||||
|
|> Zipper.subtree()
|
||||||
|
|> Zipper.root()
|
||||||
|
|> func.()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -303,7 +454,7 @@ defmodule Igniter.Common do
|
||||||
|> Module.split()
|
|> Module.split()
|
||||||
|> Enum.map(&String.to_atom/1)
|
|> Enum.map(&String.to_atom/1)
|
||||||
|
|
||||||
with zipper when not is_nil(zipper) <- find_pattern(zipper, {:defmodule, _, [_, _]}),
|
with {:ok, zipper} <- move_to_pattern(zipper, {:defmodule, _, [_, _]}),
|
||||||
subtree <- Zipper.subtree(zipper),
|
subtree <- Zipper.subtree(zipper),
|
||||||
subtree <- subtree |> Zipper.down() |> Zipper.rightmost(),
|
subtree <- subtree |> Zipper.down() |> Zipper.rightmost(),
|
||||||
subtree <- remove_module_definitions(subtree),
|
subtree <- remove_module_definitions(subtree),
|
||||||
|
@ -347,42 +498,42 @@ defmodule Igniter.Common do
|
||||||
defp do_equal_modules?(_, _), do: false
|
defp do_equal_modules?(_, _), do: false
|
||||||
|
|
||||||
def move_to_defp(zipper, fun, arity) do
|
def move_to_defp(zipper, fun, arity) do
|
||||||
case find_pattern(zipper, {:defp, _, [{^fun, _, args}, _]} when length(args) == arity) do
|
case move_to_pattern(zipper, {:defp, _, [{^fun, _, args}, _]} when length(args) == arity) do
|
||||||
nil ->
|
:error ->
|
||||||
if arity == 0 do
|
if arity == 0 do
|
||||||
case find_pattern(zipper, {:defp, _, [{^fun, _, context}, _]} when is_atom(context)) do
|
case move_to_pattern(zipper, {:defp, _, [{^fun, _, context}, _]} when is_atom(context)) do
|
||||||
nil ->
|
:error ->
|
||||||
:error
|
:error
|
||||||
|
|
||||||
zipper ->
|
{:ok, zipper} ->
|
||||||
move_to_do_block(zipper)
|
move_to_do_block(zipper)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
:error
|
:error
|
||||||
end
|
end
|
||||||
|
|
||||||
zipper ->
|
{:ok, zipper} ->
|
||||||
move_to_do_block(zipper)
|
move_to_do_block(zipper)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def move_to_do_block(zipper) do
|
def move_to_do_block(zipper) do
|
||||||
case find_pattern(zipper, {{:__block__, _, [:do]}, _}) do
|
case move_to_pattern(zipper, {{:__block__, _, [:do]}, _}) do
|
||||||
nil ->
|
:error ->
|
||||||
:error
|
:error
|
||||||
|
|
||||||
zipper ->
|
{:ok, zipper} ->
|
||||||
{:ok,
|
{:ok,
|
||||||
zipper
|
zipper
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> Zipper.rightmost()
|
|> Zipper.rightmost()
|
||||||
|> maybe_enter_block()}
|
|> maybe_move_to_block()}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_enter_block(nil), do: nil
|
def maybe_move_to_block(nil), do: nil
|
||||||
|
|
||||||
def maybe_enter_block(zipper) do
|
def maybe_move_to_block(zipper) do
|
||||||
zipper
|
zipper
|
||||||
|> Zipper.subtree()
|
|> Zipper.subtree()
|
||||||
|> Zipper.root()
|
|> Zipper.root()
|
||||||
|
@ -396,7 +547,7 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_module_definitions(zipper) do
|
def remove_module_definitions(zipper) do
|
||||||
Sourceror.Zipper.traverse(zipper, fn
|
Zipper.traverse(zipper, fn
|
||||||
{:defmodule, _, _} ->
|
{:defmodule, _, _} ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
|
@ -411,9 +562,9 @@ defmodule Igniter.Common do
|
||||||
equality_pred.(value, quoted)
|
equality_pred.(value, quoted)
|
||||||
end)
|
end)
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
:error ->
|
||||||
zipper
|
zipper
|
||||||
|> maybe_enter_block()
|
|> maybe_move_to_block()
|
||||||
|> Zipper.insert_child(quoted)
|
|> Zipper.insert_child(quoted)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -430,13 +581,13 @@ defmodule Igniter.Common do
|
||||||
|
|
||||||
def prepend_to_list(zipper, quoted) do
|
def prepend_to_list(zipper, quoted) do
|
||||||
zipper
|
zipper
|
||||||
|> maybe_enter_block()
|
|> maybe_move_to_block()
|
||||||
|> Zipper.insert_child(quoted)
|
|> Zipper.insert_child(quoted)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_index(zipper, index) do
|
def remove_index(zipper, index) do
|
||||||
zipper
|
zipper
|
||||||
|> maybe_enter_block()
|
|> maybe_move_to_block()
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
nil ->
|
||||||
|
@ -466,7 +617,7 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp nth_right(zipper, 0) do
|
defp nth_right(zipper, 0) do
|
||||||
zipper
|
{:ok, zipper}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp nth_right(zipper, n) do
|
defp nth_right(zipper, n) do
|
||||||
|
@ -474,7 +625,7 @@ defmodule Igniter.Common do
|
||||||
|> Zipper.right()
|
|> Zipper.right()
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
nth_right(zipper, n - 1)
|
nth_right(zipper, n - 1)
|
||||||
|
@ -484,28 +635,28 @@ defmodule Igniter.Common do
|
||||||
def find_list_item_index(zipper, pred) do
|
def find_list_item_index(zipper, pred) do
|
||||||
# go into first list item
|
# go into first list item
|
||||||
zipper
|
zipper
|
||||||
|> maybe_enter_block()
|
|> maybe_move_to_block()
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
find_index_right(zipper, pred, 0)
|
find_index_right(zipper, pred, 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_list_item(zipper, pred) do
|
def move_to_list_item(zipper, pred) do
|
||||||
# go into first list item
|
# go into first list item
|
||||||
zipper
|
zipper
|
||||||
|> maybe_enter_block()
|
|> maybe_move_to_block()
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> case do
|
|> case do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
find_right(zipper, pred)
|
move_right(zipper, pred)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -522,13 +673,13 @@ defmodule Igniter.Common do
|
||||||
|
|
||||||
def tuple_elem(item, elem) do
|
def tuple_elem(item, elem) do
|
||||||
item
|
item
|
||||||
|> maybe_enter_block()
|
|> maybe_move_to_block()
|
||||||
|> Zipper.down()
|
|> Zipper.down()
|
||||||
|> go_right_n_times(elem)
|
|> go_right_n_times(elem)
|
||||||
|> maybe_enter_block()
|
|> maybe_move_to_block()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp go_right_n_times(zipper, 0), do: maybe_enter_block(zipper)
|
defp go_right_n_times(zipper, 0), do: maybe_move_to_block(zipper)
|
||||||
|
|
||||||
defp go_right_n_times(zipper, n) do
|
defp go_right_n_times(zipper, n) do
|
||||||
zipper
|
zipper
|
||||||
|
@ -540,12 +691,12 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp find_index_right(zipper, pred, index) do
|
defp find_index_right(zipper, pred, index) do
|
||||||
if pred.(maybe_enter_block(zipper)) do
|
if pred.(maybe_move_to_block(zipper)) do
|
||||||
index
|
{:ok, index}
|
||||||
else
|
else
|
||||||
case Zipper.right(zipper) do
|
case Zipper.right(zipper) do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
zipper
|
zipper
|
||||||
|
@ -554,27 +705,45 @@ defmodule Igniter.Common do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp find_right(zipper, pred) do
|
defp move_right(zipper, pred) do
|
||||||
if pred.(maybe_enter_block(zipper)) do
|
zipper_in_block = maybe_move_to_block(zipper)
|
||||||
zipper
|
|
||||||
|
if pred.(zipper_in_block) do
|
||||||
|
{:ok, zipper_in_block}
|
||||||
else
|
else
|
||||||
case Zipper.right(zipper) do
|
case Zipper.right(zipper) do
|
||||||
nil ->
|
nil ->
|
||||||
nil
|
:error
|
||||||
|
|
||||||
zipper ->
|
zipper ->
|
||||||
zipper
|
zipper
|
||||||
|> find_right(pred)
|
|> move_right(pred)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def keywordify([], value) do
|
def keywordify([], value) do
|
||||||
value
|
{:__block__, [], [value]}
|
||||||
end
|
end
|
||||||
|
|
||||||
def keywordify([key | rest], value) do
|
def keywordify([key | rest], value) do
|
||||||
[{key, keywordify(rest, value)}]
|
[{{:__block__, [format: :keyword], [key]}, [keywordify(rest, value)]}]
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def mappify([], value) do
|
||||||
|
{:__block__, [], [value]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def mappify([key | rest], value) do
|
||||||
|
format =
|
||||||
|
if is_atom(key) do
|
||||||
|
:keyword
|
||||||
|
else
|
||||||
|
:map
|
||||||
|
end
|
||||||
|
|
||||||
|
[{{:__block__, [format: format], [key]}, [mappify(rest, value)]}]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,14 +26,10 @@ defmodule Igniter.Config do
|
||||||
|
|
||||||
:error ->
|
:error ->
|
||||||
# add new code here
|
# add new code here
|
||||||
|
[first | rest] = config_path
|
||||||
|
|
||||||
config =
|
config =
|
||||||
if Enum.count(config_path) == 1 do
|
{:config, [], [app_name, [{first, Igniter.Common.keywordify(rest, value)}]]}
|
||||||
quote do
|
|
||||||
config unquote(app_name), unquote(Enum.at(config_path, 0)), unquote(value)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
{:config, [], [app_name, Igniter.Common.keywordify(config_path, value)]}
|
|
||||||
end
|
|
||||||
|
|
||||||
code =
|
code =
|
||||||
zipper
|
zipper
|
||||||
|
@ -55,21 +51,15 @@ defmodule Igniter.Config do
|
||||||
if Enum.count(config_path) == 1 do
|
if Enum.count(config_path) == 1 do
|
||||||
config_item = Enum.at(config_path, 0)
|
config_item = Enum.at(config_path, 0)
|
||||||
|
|
||||||
case Common.find_function_call_in_current_scope(zipper, :config, 3, fn function_call ->
|
case Common.move_to_function_call_in_current_scope(zipper, :config, 3, fn function_call ->
|
||||||
Common.argument_matches_pattern?(function_call, 0, ^app_name)
|
Common.argument_matches_pattern?(function_call, 0, ^app_name) &&
|
||||||
Common.argument_matches_pattern?(function_call, 1, ^config_item)
|
Common.argument_matches_pattern?(function_call, 1, ^config_item)
|
||||||
end) do
|
end) do
|
||||||
nil ->
|
:error ->
|
||||||
:error
|
:error
|
||||||
|
|
||||||
zipper ->
|
{:ok, zipper} ->
|
||||||
case Common.update_nth_argument(zipper, 2, updater) do
|
Common.update_nth_argument(zipper, 2, updater)
|
||||||
nil ->
|
|
||||||
:error
|
|
||||||
|
|
||||||
zipper ->
|
|
||||||
{:ok, zipper}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
:error
|
:error
|
||||||
|
@ -77,23 +67,22 @@ defmodule Igniter.Config do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp try_update_two_arg(zipper, config_path, app_name, value, updater) do
|
defp try_update_two_arg(zipper, config_path, app_name, value, updater) do
|
||||||
case Common.find_function_call_in_current_scope(zipper, :config, 2, fn function_call ->
|
case Common.move_to_function_call_in_current_scope(zipper, :config, 2, fn function_call ->
|
||||||
Common.argument_matches_pattern?(function_call, 0, ^app_name)
|
Common.argument_matches_pattern?(function_call, 0, ^app_name)
|
||||||
end) do
|
end) do
|
||||||
nil ->
|
:error ->
|
||||||
:error
|
:error
|
||||||
|
|
||||||
zipper ->
|
{:ok, zipper} ->
|
||||||
Common.update_nth_argument(zipper, 1, fn zipper ->
|
Common.update_nth_argument(zipper, 1, fn zipper ->
|
||||||
Igniter.Common.put_in_keyword(zipper, config_path, value, updater)
|
case Igniter.Common.put_in_keyword(zipper, config_path, value, updater) do
|
||||||
end)
|
{:ok, new_zipper} ->
|
||||||
|> case do
|
new_zipper
|
||||||
nil ->
|
|
||||||
nil
|
|
||||||
|
|
||||||
zipper ->
|
_ ->
|
||||||
{:ok, zipper}
|
zipper
|
||||||
end
|
end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,8 +15,8 @@ defmodule Igniter.Deps do
|
||||||
with {:ok, zipper} <- Common.move_to_module_using(zipper, Mix.Project),
|
with {:ok, zipper} <- Common.move_to_module_using(zipper, Mix.Project),
|
||||||
{:ok, zipper} <- Common.move_to_defp(zipper, :deps, 0),
|
{:ok, zipper} <- Common.move_to_defp(zipper, :deps, 0),
|
||||||
true <- Common.node_matches_pattern?(zipper, value when is_list(value)),
|
true <- Common.node_matches_pattern?(zipper, value when is_list(value)),
|
||||||
current_declaration when not is_nil(current_declaration) <-
|
{:ok, current_declaration} <-
|
||||||
Common.find_list_item(zipper, fn item ->
|
Common.move_to_list_item(zipper, fn item ->
|
||||||
if Common.is_tuple?(item) do
|
if Common.is_tuple?(item) do
|
||||||
first_elem = Common.tuple_elem(item, 0)
|
first_elem = Common.tuple_elem(item, 0)
|
||||||
first_elem && Common.node_matches_pattern?(first_elem, ^name)
|
first_elem && Common.node_matches_pattern?(first_elem, ^name)
|
||||||
|
@ -25,7 +25,7 @@ defmodule Igniter.Deps do
|
||||||
current_declaration
|
current_declaration
|
||||||
|> Zipper.subtree()
|
|> Zipper.subtree()
|
||||||
|> Zipper.node()
|
|> Zipper.node()
|
||||||
|> Macro.to_string()
|
|> Sourceror.to_string()
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
nil
|
nil
|
||||||
|
@ -98,7 +98,6 @@ defmodule Igniter.Deps do
|
||||||
|
|
||||||
defp do_add_dependency(igniter, name, version) do
|
defp do_add_dependency(igniter, name, version) do
|
||||||
igniter
|
igniter
|
||||||
|> Igniter.Formatter.import_dep(name)
|
|
||||||
|> Igniter.update_file("mix.exs", fn source ->
|
|> Igniter.update_file("mix.exs", fn source ->
|
||||||
quoted = Rewrite.Source.get(source, :quoted)
|
quoted = Rewrite.Source.get(source, :quoted)
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,13 @@ defmodule Igniter.Formatter do
|
||||||
dep
|
dep
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|> case do
|
||||||
|
{:ok, zipper} ->
|
||||||
|
zipper
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
zipper
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|> Zipper.root()
|
|> Zipper.root()
|
||||||
|
|
||||||
|
@ -75,6 +82,13 @@ defmodule Igniter.Formatter do
|
||||||
&Igniter.Common.equal_modules?/2
|
&Igniter.Common.equal_modules?/2
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|> case do
|
||||||
|
{:ok, zipper} ->
|
||||||
|
zipper
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
zipper
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|> Zipper.root()
|
|> Zipper.root()
|
||||||
|
|
||||||
|
|
196
lib/igniter.ex
196
lib/igniter.ex
|
@ -3,7 +3,13 @@ defmodule Igniter do
|
||||||
Igniter is a library for installing packages and generating code.
|
Igniter is a library for installing packages and generating code.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
defstruct [:rewrite, issues: []]
|
defstruct [:rewrite, issues: [], tasks: []]
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
rewrite: Rewrite.t(),
|
||||||
|
issues: [String.t()],
|
||||||
|
tasks: [{String.t() | list(STring.t())}]
|
||||||
|
}
|
||||||
|
|
||||||
def new() do
|
def new() do
|
||||||
%__MODULE__{rewrite: Rewrite.new()}
|
%__MODULE__{rewrite: Rewrite.new()}
|
||||||
|
@ -13,6 +19,24 @@ defmodule Igniter do
|
||||||
%{igniter | issues: [issue | igniter.issues]}
|
%{igniter | issues: [issue | igniter.issues]}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_task(igniter, task, argv \\ []) when is_binary(task) do
|
||||||
|
%{igniter | tasks: igniter.tasks ++ [{task, argv}]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def compose_task(igniter, task, argv) when is_atom(task) do
|
||||||
|
Code.ensure_compiled!(task)
|
||||||
|
|
||||||
|
if function_exported?(task, :igniter, 2) do
|
||||||
|
if !task.supports_umbrella?() && Mix.Project.umbrella?() do
|
||||||
|
add_issue(igniter, "Cannot run #{inspect(task)} in an umbrella project.")
|
||||||
|
else
|
||||||
|
task.igniter(igniter, argv)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
add_issue(igniter, "#{inspect(task)} does not implement `Igniter.igniter/2`")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def compose_task(igniter, task_name, argv) do
|
def compose_task(igniter, task_name, argv) do
|
||||||
if igniter.issues == [] do
|
if igniter.issues == [] do
|
||||||
task_name
|
task_name
|
||||||
|
@ -22,19 +46,7 @@ defmodule Igniter do
|
||||||
igniter
|
igniter
|
||||||
|
|
||||||
task ->
|
task ->
|
||||||
Code.ensure_compiled!(task)
|
compose_task(igniter, task, argv)
|
||||||
|
|
||||||
if function_exported?(task, :igniter, 2) do
|
|
||||||
if !task.supports_umbrella?() && Mix.Project.umbrella?() do
|
|
||||||
raise """
|
|
||||||
Cannot run #{inspect(task)} in an umbrella project.
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
|
|
||||||
task.igniter(igniter, argv)
|
|
||||||
else
|
|
||||||
add_issue(igniter, "#{inspect(task)} does not implement `Igniter.igniter/2`")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
igniter
|
igniter
|
||||||
|
@ -45,9 +57,18 @@ defmodule Igniter do
|
||||||
if Rewrite.has_source?(igniter.rewrite, path) do
|
if Rewrite.has_source?(igniter.rewrite, path) do
|
||||||
%{igniter | rewrite: Rewrite.update!(igniter.rewrite, path, func)}
|
%{igniter | rewrite: Rewrite.update!(igniter.rewrite, path, func)}
|
||||||
else
|
else
|
||||||
igniter
|
if File.exists?(path) do
|
||||||
|> include_existing_elixir_file(path)
|
source = Rewrite.Source.Ex.read!(path)
|
||||||
|> update_file(path, func)
|
|
||||||
|
%{igniter | rewrite: Rewrite.put!(igniter.rewrite, source)}
|
||||||
|
|> format(path)
|
||||||
|
|> Map.update!(:rewrite, fn rewrite ->
|
||||||
|
source = Rewrite.source!(rewrite, path)
|
||||||
|
Rewrite.update!(rewrite, path, func.(source))
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
add_issue(igniter, "Required #{path} but it did not exist")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -57,6 +78,7 @@ defmodule Igniter do
|
||||||
else
|
else
|
||||||
if File.exists?(path) do
|
if File.exists?(path) do
|
||||||
%{igniter | rewrite: Rewrite.put!(igniter.rewrite, Rewrite.Source.Ex.read!(path))}
|
%{igniter | rewrite: Rewrite.put!(igniter.rewrite, Rewrite.Source.Ex.read!(path))}
|
||||||
|
|> format(path)
|
||||||
else
|
else
|
||||||
add_issue(igniter, "Required #{path} but it did not exist")
|
add_issue(igniter, "Required #{path} but it did not exist")
|
||||||
end
|
end
|
||||||
|
@ -78,6 +100,7 @@ defmodule Igniter do
|
||||||
end
|
end
|
||||||
|
|
||||||
%{igniter | rewrite: Rewrite.put!(igniter.rewrite, source)}
|
%{igniter | rewrite: Rewrite.put!(igniter.rewrite, source)}
|
||||||
|
|> format(path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -95,5 +118,144 @@ defmodule Igniter do
|
||||||
end
|
end
|
||||||
|
|
||||||
%{igniter | rewrite: Rewrite.put!(igniter.rewrite, source)}
|
%{igniter | rewrite: Rewrite.put!(igniter.rewrite, source)}
|
||||||
|
|> format(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp format(igniter, adding_path \\ nil) do
|
||||||
|
if adding_path && Path.basename(adding_path) == ".formatter.exs" do
|
||||||
|
format(igniter)
|
||||||
|
else
|
||||||
|
igniter =
|
||||||
|
"**/.formatter.exs"
|
||||||
|
|> Path.relative_to(File.cwd!())
|
||||||
|
|> Path.wildcard()
|
||||||
|
|> Enum.reduce(igniter, fn path, igniter ->
|
||||||
|
Igniter.include_existing_elixir_file(igniter, path)
|
||||||
|
end)
|
||||||
|
|
||||||
|
rewrite = igniter.rewrite
|
||||||
|
|
||||||
|
formatter_exs_files =
|
||||||
|
rewrite
|
||||||
|
|> Enum.filter(fn source ->
|
||||||
|
source
|
||||||
|
|> Rewrite.Source.get(:path)
|
||||||
|
|> Path.basename()
|
||||||
|
|> Kernel.==(".formatter.exs")
|
||||||
|
end)
|
||||||
|
|> Map.new(fn source ->
|
||||||
|
dir =
|
||||||
|
source
|
||||||
|
|> Rewrite.Source.get(:path)
|
||||||
|
|> Path.dirname()
|
||||||
|
|
||||||
|
{dir, source}
|
||||||
|
end)
|
||||||
|
|
||||||
|
rewrite =
|
||||||
|
Rewrite.map!(rewrite, fn source ->
|
||||||
|
path = source |> Rewrite.Source.get(:path)
|
||||||
|
|
||||||
|
if is_nil(adding_path) || path == adding_path do
|
||||||
|
dir = Path.dirname(path)
|
||||||
|
|
||||||
|
case find_formatter_exs_file_options(dir, formatter_exs_files) do
|
||||||
|
:error ->
|
||||||
|
source
|
||||||
|
|
||||||
|
{:ok, opts} ->
|
||||||
|
formatted = Rewrite.Source.Ex.format(source, opts)
|
||||||
|
|
||||||
|
source
|
||||||
|
|> Rewrite.Source.Ex.put_formatter_opts(opts)
|
||||||
|
|> Rewrite.Source.update(:content, formatted)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
source
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
%{igniter | rewrite: rewrite}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp find_formatter_exs_file_options(path, formatter_exs_files) do
|
||||||
|
case Map.fetch(formatter_exs_files, path) do
|
||||||
|
{:ok, source} ->
|
||||||
|
{opts, _} = Rewrite.Source.get(source, :quoted) |> Code.eval_quoted()
|
||||||
|
|
||||||
|
{:ok, eval_deps(opts)}
|
||||||
|
|
||||||
|
:error ->
|
||||||
|
if path in ["/", "."] do
|
||||||
|
:error
|
||||||
|
else
|
||||||
|
new_path =
|
||||||
|
Path.join(path, "..")
|
||||||
|
|> Path.expand()
|
||||||
|
|> Path.relative_to_cwd()
|
||||||
|
|
||||||
|
find_formatter_exs_file_options(new_path, formatter_exs_files)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This can be removed if/when this PR is merged: https://github.com/hrzndhrn/rewrite/pull/34
|
||||||
|
defp eval_deps(formatter_opts) do
|
||||||
|
deps = Keyword.get(formatter_opts, :import_deps, [])
|
||||||
|
|
||||||
|
locals_without_parens = eval_deps_opts(deps)
|
||||||
|
|
||||||
|
formatter_opts =
|
||||||
|
Keyword.update(
|
||||||
|
formatter_opts,
|
||||||
|
:locals_without_parens,
|
||||||
|
locals_without_parens,
|
||||||
|
&(locals_without_parens ++ &1)
|
||||||
|
)
|
||||||
|
|
||||||
|
formatter_opts
|
||||||
|
end
|
||||||
|
|
||||||
|
defp eval_deps_opts([]) do
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp eval_deps_opts(deps) do
|
||||||
|
deps_paths = Mix.Project.deps_paths()
|
||||||
|
|
||||||
|
for dep <- deps,
|
||||||
|
dep_path = fetch_valid_dep_path(dep, deps_paths),
|
||||||
|
!is_nil(dep_path),
|
||||||
|
dep_dot_formatter = Path.join(dep_path, ".formatter.exs"),
|
||||||
|
File.regular?(dep_dot_formatter),
|
||||||
|
dep_opts = eval_file_with_keyword_list(dep_dot_formatter),
|
||||||
|
parenless_call <- dep_opts[:export][:locals_without_parens] || [],
|
||||||
|
uniq: true,
|
||||||
|
do: parenless_call
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_valid_dep_path(dep, deps_paths) when is_atom(dep) do
|
||||||
|
with %{^dep => path} <- deps_paths,
|
||||||
|
true <- File.dir?(path) do
|
||||||
|
path
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_valid_dep_path(_dep, _deps_paths) do
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
defp eval_file_with_keyword_list(path) do
|
||||||
|
{opts, _} = Code.eval_file(path)
|
||||||
|
|
||||||
|
unless Keyword.keyword?(opts) do
|
||||||
|
raise "Expected #{inspect(path)} to return a keyword list, got: #{inspect(opts)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
opts
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
130
lib/install.ex
Normal file
130
lib/install.ex
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
defmodule Igniter.Install do
|
||||||
|
@option_schema [
|
||||||
|
switches: [
|
||||||
|
no_network: :boolean,
|
||||||
|
example: :boolean,
|
||||||
|
dry_run: :boolean
|
||||||
|
],
|
||||||
|
aliases: [
|
||||||
|
d: :dry_run,
|
||||||
|
n: :no_network,
|
||||||
|
e: :example
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
# only supports hex installation at the moment
|
||||||
|
def install(install, argv) do
|
||||||
|
install_list =
|
||||||
|
install
|
||||||
|
|> String.split(",")
|
||||||
|
|> Enum.map(&String.to_atom/1)
|
||||||
|
|
||||||
|
Application.ensure_all_started(:req)
|
||||||
|
|
||||||
|
{options, _} =
|
||||||
|
OptionParser.parse!(argv, @option_schema)
|
||||||
|
|
||||||
|
argv = OptionParser.to_argv(options)
|
||||||
|
|
||||||
|
igniter = Igniter.new()
|
||||||
|
|
||||||
|
igniter =
|
||||||
|
Enum.reduce(install_list, igniter, fn install, igniter ->
|
||||||
|
if Mix.Project.config()[:deps][install][:path] do
|
||||||
|
Mix.shell().info(
|
||||||
|
"Not looking up dependency for #{install}, because a local dependency is detected"
|
||||||
|
)
|
||||||
|
|
||||||
|
igniter
|
||||||
|
else
|
||||||
|
case Req.get!("https://hex.pm/api/packages/#{install}").body do
|
||||||
|
%{
|
||||||
|
"releases" => [
|
||||||
|
%{"version" => version}
|
||||||
|
| _
|
||||||
|
]
|
||||||
|
} ->
|
||||||
|
requirement =
|
||||||
|
version
|
||||||
|
|> Version.parse!()
|
||||||
|
|> case do
|
||||||
|
%Version{major: 0, minor: minor} ->
|
||||||
|
"~> 0.#{minor}"
|
||||||
|
|
||||||
|
%Version{major: major} ->
|
||||||
|
"~> #{major}.0"
|
||||||
|
end
|
||||||
|
|
||||||
|
Igniter.Deps.add_dependency(igniter, install, requirement)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Igniter.add_issue(igniter, "No published versions of #{install} on hex")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
confirmation_message =
|
||||||
|
unless options[:dry_run] do
|
||||||
|
"Dependencies changes must go into effect before individual installers can be run. Proceed with changes?"
|
||||||
|
end
|
||||||
|
|
||||||
|
dependency_add_result =
|
||||||
|
Igniter.Tasks.do_or_dry_run(igniter, argv,
|
||||||
|
title: "Fetching Dependency",
|
||||||
|
quiet_on_no_changes?: true,
|
||||||
|
confirmation_message: confirmation_message
|
||||||
|
)
|
||||||
|
|
||||||
|
if dependency_add_result == :issues do
|
||||||
|
raise "Exiting due to issues found while fetching dependency"
|
||||||
|
end
|
||||||
|
|
||||||
|
if dependency_add_result == :dry_run_with_changes do
|
||||||
|
install_dep_now? =
|
||||||
|
Mix.shell().yes?("""
|
||||||
|
Cannot run any associated installers for the requested packages without
|
||||||
|
commiting changes and fetching dependencies.
|
||||||
|
|
||||||
|
Would you like to do so now? The remaining steps will be displayed as a dry run.
|
||||||
|
""")
|
||||||
|
|
||||||
|
if install_dep_now? do
|
||||||
|
Igniter.Tasks.do_or_dry_run(igniter, (argv ++ ["--yes"]) -- ["--dry-run"],
|
||||||
|
title: "Fetching Dependency",
|
||||||
|
quiet_on_no_changes?: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Mix.shell().info("running mix deps.get")
|
||||||
|
|
||||||
|
case Mix.shell().cmd("mix deps.get") do
|
||||||
|
0 ->
|
||||||
|
Mix.Task.reenable("compile")
|
||||||
|
Mix.Task.run("compile")
|
||||||
|
|
||||||
|
exit_code ->
|
||||||
|
Mix.shell().info("""
|
||||||
|
mix deps.get returned exited with code: `#{exit_code}`
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
|
||||||
|
all_tasks =
|
||||||
|
Enum.filter(Mix.Task.load_all(), &Spark.implements_behaviour?(&1, Igniter.Mix.Task))
|
||||||
|
|
||||||
|
install_list
|
||||||
|
|> Enum.flat_map(fn install ->
|
||||||
|
all_tasks
|
||||||
|
|> Enum.find(fn task ->
|
||||||
|
Mix.Task.task_name(task) == "#{install}.install"
|
||||||
|
end)
|
||||||
|
|> List.wrap()
|
||||||
|
end)
|
||||||
|
|> Enum.reduce(Igniter.new(), fn task, igniter ->
|
||||||
|
Igniter.compose_task(igniter, task, argv)
|
||||||
|
end)
|
||||||
|
|> Igniter.Tasks.do_or_dry_run(argv)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,11 +5,7 @@ defmodule Mix.Tasks.Igniter.Install do
|
||||||
def run([install | argv]) do
|
def run([install | argv]) do
|
||||||
Application.ensure_all_started([:rewrite])
|
Application.ensure_all_started([:rewrite])
|
||||||
|
|
||||||
if String.contains?(install, "/") do
|
Igniter.Install.install(install, argv)
|
||||||
raise "installation from github not supported yet"
|
|
||||||
else
|
|
||||||
Mix.Task.run("igniter.install_from_hex", [install | argv])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def run([]) do
|
def run([]) do
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
defmodule Mix.Tasks.Igniter.InstallFromHex do
|
|
||||||
use Mix.Task
|
|
||||||
|
|
||||||
@impl true
|
|
||||||
def run([install | argv]) do
|
|
||||||
install = String.to_atom(install)
|
|
||||||
Application.ensure_all_started(:req)
|
|
||||||
|
|
||||||
case Req.get!("https://hex.pm/api/packages/#{install}").body do
|
|
||||||
%{
|
|
||||||
"releases" => [
|
|
||||||
%{"version" => version}
|
|
||||||
| _
|
|
||||||
]
|
|
||||||
} ->
|
|
||||||
requirement =
|
|
||||||
version
|
|
||||||
|> Version.parse!()
|
|
||||||
|> case do
|
|
||||||
%Version{major: 0, minor: minor} ->
|
|
||||||
"~> 0.#{minor}"
|
|
||||||
|
|
||||||
%Version{major: major} ->
|
|
||||||
"~> #{major}.0"
|
|
||||||
end
|
|
||||||
|
|
||||||
dependency_add_result =
|
|
||||||
Igniter.new()
|
|
||||||
|> Igniter.Deps.add_dependency(install, requirement)
|
|
||||||
|> Igniter.Tasks.do_or_dry_run(argv,
|
|
||||||
title: "Fetching Dependency",
|
|
||||||
quiet_on_no_changes?: true
|
|
||||||
)
|
|
||||||
|
|
||||||
if dependency_add_result == :issues do
|
|
||||||
raise "Exiting due to issues found while fetching dependency"
|
|
||||||
end
|
|
||||||
|
|
||||||
if dependency_add_result == :dry_run_with_changes do
|
|
||||||
install_dep_now? =
|
|
||||||
Mix.shell().yes?("""
|
|
||||||
Cannot display any further installation changes without installing the `#{install}` dependency.
|
|
||||||
|
|
||||||
Would you like to install the dependency now?
|
|
||||||
|
|
||||||
This will be the only change made, and then any remaining steps will be displayed as a dry-run.
|
|
||||||
""")
|
|
||||||
|
|
||||||
if install_dep_now? do
|
|
||||||
Igniter.new()
|
|
||||||
|> Igniter.Deps.add_dependency(install, requirement)
|
|
||||||
|> Igniter.Tasks.do_or_dry_run(argv -- ["--dry-run"],
|
|
||||||
title: "Fetching Dependency",
|
|
||||||
quiet_on_no_changes?: true
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
case System.cmd("mix", ["deps.get"]) do
|
|
||||||
{_, 0} ->
|
|
||||||
:ok
|
|
||||||
|
|
||||||
{output, exit} ->
|
|
||||||
Mix.shell().info("""
|
|
||||||
mix deps.get returned exited with code: `#{exit}`
|
|
||||||
|
|
||||||
#{output}
|
|
||||||
""")
|
|
||||||
end
|
|
||||||
|
|
||||||
Mix.Task.load_all()
|
|
||||||
|> Enum.find(fn module ->
|
|
||||||
Mix.Task.task_name(module) == "igniter.install.#{install}"
|
|
||||||
end)
|
|
||||||
|> case do
|
|
||||||
nil ->
|
|
||||||
if dependency_add_result in [:dry_run_with_no_changes, :no_changes] do
|
|
||||||
Mix.shell().info("Igniter: #{install} already installed")
|
|
||||||
else
|
|
||||||
if dependency_add_result == :changes_aborted do
|
|
||||||
Mix.shell().info("Igniter: #{install} installation aborted")
|
|
||||||
else
|
|
||||||
Mix.shell().info("Igniter: #{install} installation complete")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
_task ->
|
|
||||||
Mix.shell().info("Igniter: Installing #{install}...")
|
|
||||||
Mix.Task.run("igniter.install.#{install}", argv)
|
|
||||||
end
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
raise "No published versions of #{install}"
|
|
||||||
end
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,4 +1,4 @@
|
||||||
defmodule Mix.Tasks.Igniter.Install.Spark do
|
defmodule Mix.Tasks.Spark.Install do
|
||||||
use Igniter.Mix.Task
|
use Igniter.Mix.Task
|
||||||
|
|
||||||
def igniter(igniter, _argv) do
|
def igniter(igniter, _argv) do
|
109
lib/tasks.ex
109
lib/tasks.ex
|
@ -4,6 +4,7 @@ defmodule Igniter.Tasks do
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_or_dry_run(igniter, argv, opts \\ []) do
|
def do_or_dry_run(igniter, argv, opts \\ []) do
|
||||||
|
igniter = %{igniter | issues: Enum.uniq(igniter.issues)}
|
||||||
title = opts[:title] || "Igniter"
|
title = opts[:title] || "Igniter"
|
||||||
|
|
||||||
sources =
|
sources =
|
||||||
|
@ -19,7 +20,7 @@ defmodule Igniter.Tasks do
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
issues = changed_issues ++ Rewrite.Source.issues(source)
|
issues = Enum.uniq(changed_issues ++ Rewrite.Source.issues(source))
|
||||||
|
|
||||||
case issues do
|
case issues do
|
||||||
[] -> []
|
[] -> []
|
||||||
|
@ -42,29 +43,73 @@ defmodule Igniter.Tasks do
|
||||||
|> case do
|
|> case do
|
||||||
[] ->
|
[] ->
|
||||||
unless opts[:quiet_on_no_changes?] do
|
unless opts[:quiet_on_no_changes?] do
|
||||||
IO.puts("\n#{title}: No proposed changes!\n")
|
Mix.shell().info("\n#{title}: No proposed changes!\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
:dry_run_with_no_changes
|
:dry_run_with_no_changes
|
||||||
|
|
||||||
sources ->
|
sources ->
|
||||||
IO.puts("\n#{title}: Proposed changes:\n")
|
Mix.shell().info("\n#{title}: Proposed changes:\n")
|
||||||
|
|
||||||
Enum.each(sources, fn source ->
|
Enum.each(sources, fn source ->
|
||||||
IO.puts("""
|
if Rewrite.Source.from?(source, :string) do
|
||||||
#{Rewrite.Source.get(source, :path)}
|
content_lines =
|
||||||
|
source
|
||||||
|
|> Rewrite.Source.get(:content)
|
||||||
|
|> String.split("\n")
|
||||||
|
|> Enum.with_index()
|
||||||
|
|
||||||
#{Rewrite.Source.diff(source)}
|
space_padding =
|
||||||
""")
|
content_lines
|
||||||
|
|> Enum.map(&elem(&1, 1))
|
||||||
|
|> Enum.max()
|
||||||
|
|> to_string()
|
||||||
|
|> String.length()
|
||||||
|
|
||||||
|
diffish_looking_text =
|
||||||
|
Enum.map_join(content_lines, "\n", fn {line, line_number_minus_one} ->
|
||||||
|
line_number = line_number_minus_one + 1
|
||||||
|
|
||||||
|
"#{String.pad_trailing(to_string(line_number), space_padding)} #{IO.ANSI.yellow()}| #{IO.ANSI.green()}#{line}#{IO.ANSI.reset()}"
|
||||||
|
end)
|
||||||
|
|
||||||
|
Mix.shell().info("""
|
||||||
|
Create: #{Rewrite.Source.get(source, :path)}
|
||||||
|
|
||||||
|
#{diffish_looking_text}
|
||||||
|
""")
|
||||||
|
else
|
||||||
|
Mix.shell().info("""
|
||||||
|
Update: #{Rewrite.Source.get(source, :path)}
|
||||||
|
|
||||||
|
#{Rewrite.Source.diff(source)}
|
||||||
|
""")
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
:dry_run_with_changes
|
:dry_run_with_changes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if igniter.tasks != [] do
|
||||||
|
message =
|
||||||
|
if result_of_dry_run in [:dry_run_with_no_changes, :no_changes] do
|
||||||
|
"The following tasks will be run"
|
||||||
|
else
|
||||||
|
"The following tasks will be run after the above changes:"
|
||||||
|
end
|
||||||
|
|
||||||
|
Mix.shell().info("""
|
||||||
|
#{message}
|
||||||
|
|
||||||
|
#{Enum.map_join(igniter.tasks, "\n", fn {task, args} -> "* #{IO.ANSI.red()}#{task}#{IO.ANSI.yellow()} #{Enum.join(args, " ")}#{IO.ANSI.reset()}" end)}
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
|
||||||
if "--dry-run" in argv || result_of_dry_run == :dry_run_with_no_changes do
|
if "--dry-run" in argv || result_of_dry_run == :dry_run_with_no_changes do
|
||||||
result_of_dry_run
|
result_of_dry_run
|
||||||
else
|
else
|
||||||
if "--yes" in argv || Mix.shell().yes?("Proceed with changes?") do
|
if "--yes" in argv ||
|
||||||
|
Mix.shell().yes?(opts[:confirmation_message] || "Proceed with changes?") do
|
||||||
sources
|
sources
|
||||||
|> Enum.any?(fn source ->
|
|> Enum.any?(fn source ->
|
||||||
Rewrite.Source.updated?(source)
|
Rewrite.Source.updated?(source)
|
||||||
|
@ -72,8 +117,22 @@ defmodule Igniter.Tasks do
|
||||||
|> if do
|
|> if do
|
||||||
igniter.rewrite
|
igniter.rewrite
|
||||||
|> Rewrite.write_all()
|
|> Rewrite.write_all()
|
||||||
|
|> case do
|
||||||
|
{:ok, _result} ->
|
||||||
|
igniter.tasks
|
||||||
|
|> Enum.each(fn {task, args} ->
|
||||||
|
Mix.Task.run(task, args)
|
||||||
|
end)
|
||||||
|
|
||||||
:changes_made
|
:changes_made
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
igniter
|
||||||
|
|> Igniter.add_issue(error)
|
||||||
|
|> igniter_issues()
|
||||||
|
|
||||||
|
{:error, error}
|
||||||
|
end
|
||||||
else
|
else
|
||||||
:no_changes
|
:no_changes
|
||||||
end
|
end
|
||||||
|
@ -82,26 +141,30 @@ defmodule Igniter.Tasks do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
IO.puts("Issues during code generation")
|
igniter_issues(igniter)
|
||||||
|
|
||||||
igniter.issues
|
|
||||||
|> Enum.map_join("\n", fn error ->
|
|
||||||
if is_binary(error) do
|
|
||||||
"* #{error}"
|
|
||||||
else
|
|
||||||
"* #{Exception.format(:error, error)}"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|> IO.puts()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp igniter_issues(igniter) do
|
||||||
|
Mix.shell().info("Issues during code generation")
|
||||||
|
|
||||||
|
igniter.issues
|
||||||
|
|> Enum.map_join("\n", fn error ->
|
||||||
|
if is_binary(error) do
|
||||||
|
"* #{error}"
|
||||||
|
else
|
||||||
|
"* #{Exception.format(:error, error)}"
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Mix.shell().info()
|
||||||
|
end
|
||||||
|
|
||||||
defp explain_issues(issues) do
|
defp explain_issues(issues) do
|
||||||
IO.puts("Igniter: Issues found in proposed changes:\n")
|
Mix.shell().info("Igniter: Issues found in proposed changes:\n")
|
||||||
|
|
||||||
Enum.each(issues, fn {source, issues} ->
|
Enum.each(issues, fn {source, issues} ->
|
||||||
IO.puts("Issues with #{Rewrite.Source.get(source, :path)}")
|
Mix.shell().info("Issues with #{Rewrite.Source.get(source, :path)}")
|
||||||
|
|
||||||
issues
|
issues
|
||||||
|> Enum.map_join("\n", fn error ->
|
|> Enum.map_join("\n", fn error ->
|
||||||
|
@ -111,7 +174,7 @@ defmodule Igniter.Tasks do
|
||||||
"* #{Exception.format(:error, error)}"
|
"* #{Exception.format(:error, error)}"
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> IO.puts()
|
|> Mix.shell().info()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
2
mix.exs
2
mix.exs
|
@ -28,7 +28,7 @@ defmodule Igniter.MixProject do
|
||||||
logo: "logos/igniter-logo.png",
|
logo: "logos/igniter-logo.png",
|
||||||
extra_section: "GUIDES",
|
extra_section: "GUIDES",
|
||||||
extras: [
|
extras: [
|
||||||
{"README.md", title: "Home"},
|
{"README.md", title: "Home"}
|
||||||
# "CHANGELOG.md"
|
# "CHANGELOG.md"
|
||||||
],
|
],
|
||||||
before_closing_head_tag: fn type ->
|
before_closing_head_tag: fn type ->
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -38,7 +38,7 @@
|
||||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||||
"reactor": {:hex, :reactor, "0.8.4", "344d02ba4a0010763851f4e4aa0ff190ebe7e392e3c27c6cd143dde077b986e7", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "49c1fd3c786603cec8140ce941c41c7ea72cc4411860ccdee9876c4ca2204f81"},
|
"reactor": {:hex, :reactor, "0.8.4", "344d02ba4a0010763851f4e4aa0ff190ebe7e392e3c27c6cd143dde077b986e7", [:mix], [{:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "49c1fd3c786603cec8140ce941c41c7ea72cc4411860ccdee9876c4ca2204f81"},
|
||||||
"req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"},
|
"req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"},
|
||||||
"rewrite": {:hex, :rewrite, "0.10.1", "238073297d122dad6b5501d761cb3bc0ce5bb4ab86e34c826c395f5f44b2f562", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "91f8d6fe363033e8ff60097bb5e0b76867667df0b4d67e79c2850444c02d8b19"},
|
"rewrite": {:hex, :rewrite, "0.10.3", "1c998cceac960c3025a1701158d846dee94bc426d95abefd2b4a2e981835ea1c", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "d3ea3179de167ebda56bf81b7e5c2697256a0719fdcc2c0df65ea8173efe3563"},
|
||||||
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
|
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
|
||||||
"sourceror": {:hex, :sourceror, "1.2.1", "b415255ad8bd05f0e859bb3d7ea617f6c2a4a405f2a534a231f229bd99b89f8b", [:mix], [], "hexpm", "e4d97087e67584a7585b5fe3d5a71bf8e7332f795dd1a44983d750003d5e750c"},
|
"sourceror": {:hex, :sourceror, "1.2.1", "b415255ad8bd05f0e859bb3d7ea617f6c2a4a405f2a534a231f229bd99b89f8b", [:mix], [], "hexpm", "e4d97087e67584a7585b5fe3d5a71bf8e7332f795dd1a44983d750003d5e750c"},
|
||||||
"spark": {:hex, :spark, "2.1.22", "a36400eede64c51af578de5fdb5a5aaa3e0811da44bcbe7545fce059bd2a990b", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "f764611d0b15ac132e72b2326539acc11fc4e63baa3e429f541bca292b5f7064"},
|
"spark": {:hex, :spark, "2.1.22", "a36400eede64c51af578de5fdb5a5aaa3e0811da44bcbe7545fce059bd2a990b", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "f764611d0b15ac132e72b2326539acc11fc4e63baa3e429f541bca292b5f7064"},
|
||||||
|
|
125
test/config_test.exs
Normal file
125
test/config_test.exs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
defmodule Igniter.ConfigTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
|
||||||
|
alias Rewrite.Source
|
||||||
|
|
||||||
|
describe "configure/6" do
|
||||||
|
test "it creates the config file if it does not exist" do
|
||||||
|
%{rewrite: rewrite} =
|
||||||
|
Igniter.Config.configure(Igniter.new(), "fake.exs", :fake, [:foo, :bar], "baz")
|
||||||
|
|
||||||
|
config_file = Rewrite.source!(rewrite, "config/fake.exs")
|
||||||
|
|
||||||
|
assert Source.from?(config_file, :string)
|
||||||
|
|
||||||
|
assert Source.get(config_file, :content) == """
|
||||||
|
import Config
|
||||||
|
config :fake, foo: [bar: "baz"]
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it merges with 2 arg version of existing config" do
|
||||||
|
%{rewrite: rewrite} =
|
||||||
|
Igniter.new()
|
||||||
|
|> Igniter.create_new_elixir_file("config/fake.exs", """
|
||||||
|
config :fake, buz: [:blat]
|
||||||
|
""")
|
||||||
|
|> Igniter.Config.configure("fake.exs", :fake, [:foo, :bar], "baz")
|
||||||
|
|
||||||
|
config_file = Rewrite.source!(rewrite, "config/fake.exs")
|
||||||
|
|
||||||
|
assert Source.get(config_file, :content) == """
|
||||||
|
config :fake, foo: [bar: "baz"], buz: [:blat]
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it merges with 2 arg version of existing config with a single path item" do
|
||||||
|
%{rewrite: rewrite} =
|
||||||
|
Igniter.new()
|
||||||
|
|> Igniter.create_new_elixir_file("config/fake.exs", """
|
||||||
|
config :fake, buz: [:blat]
|
||||||
|
""")
|
||||||
|
|> Igniter.Config.configure("fake.exs", :fake, [:foo], "baz")
|
||||||
|
|
||||||
|
config_file = Rewrite.source!(rewrite, "config/fake.exs")
|
||||||
|
|
||||||
|
assert Source.get(config_file, :content) == """
|
||||||
|
config :fake, foo: "baz", buz: [:blat]
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it merges with 3 arg version of existing config" do
|
||||||
|
%{rewrite: rewrite} =
|
||||||
|
Igniter.new()
|
||||||
|
|> Igniter.create_new_elixir_file("config/fake.exs", """
|
||||||
|
config :fake, :buz, [:blat]
|
||||||
|
""")
|
||||||
|
|> Igniter.Config.configure("fake.exs", :fake, [:foo, :bar], "baz")
|
||||||
|
|
||||||
|
config_file = Rewrite.source!(rewrite, "config/fake.exs")
|
||||||
|
|
||||||
|
assert Source.get(config_file, :content) == """
|
||||||
|
config :fake, :buz, [:blat]
|
||||||
|
config :fake, foo: [bar: "baz"]
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it merges with 3 arg version of existing config with a single path item" do
|
||||||
|
%{rewrite: rewrite} =
|
||||||
|
Igniter.new()
|
||||||
|
|> Igniter.create_new_elixir_file("config/fake.exs", """
|
||||||
|
config :fake, :buz, [:blat]
|
||||||
|
""")
|
||||||
|
|> Igniter.Config.configure("fake.exs", :fake, [:foo], "baz")
|
||||||
|
|
||||||
|
config_file = Rewrite.source!(rewrite, "config/fake.exs")
|
||||||
|
|
||||||
|
assert Source.get(config_file, :content) == """
|
||||||
|
config :fake, :buz, [:blat]
|
||||||
|
config :fake, foo: "baz"
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
test "present values can be updated" do
|
||||||
|
%{rewrite: rewrite} =
|
||||||
|
Igniter.new()
|
||||||
|
|> Igniter.create_new_elixir_file("config/fake.exs", """
|
||||||
|
config :fake, :buz, [:blat]
|
||||||
|
""")
|
||||||
|
|> Igniter.Config.configure("fake.exs", :fake, [:buz], "baz", fn list ->
|
||||||
|
Igniter.Common.prepend_new_to_list(list, "baz")
|
||||||
|
end)
|
||||||
|
|
||||||
|
config_file = Rewrite.source!(rewrite, "config/fake.exs")
|
||||||
|
|
||||||
|
assert Source.get(config_file, :content) == """
|
||||||
|
config :fake, :buz, ["baz", :blat]
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
test "present values can be updated by updating map keys" do
|
||||||
|
%{rewrite: rewrite} =
|
||||||
|
Igniter.new()
|
||||||
|
|> Igniter.create_new_elixir_file("config/fake.exs", """
|
||||||
|
config :fake, foo: %{"a" => ["a", "b"]}
|
||||||
|
""")
|
||||||
|
|> Igniter.Config.configure("fake.exs", :fake, [:foo], %{"b" => ["c", "d"]}, fn zipper ->
|
||||||
|
Igniter.Common.set_map_key(zipper, "b", ["c", "d"], fn zipper ->
|
||||||
|
zipper
|
||||||
|
|> Igniter.Common.prepend_new_to_list(zipper, "c")
|
||||||
|
|> Igniter.Common.prepend_new_to_list(zipper, "d")
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
{:ok, zipper} -> zipper
|
||||||
|
_ -> zipper
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
config_file = Rewrite.source!(rewrite, "config/fake.exs")
|
||||||
|
|
||||||
|
assert Source.get(config_file, :content) == """
|
||||||
|
config :fake, foo: %{"b" => ["c", "d"], "a" => ["a", "b"]}
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,8 +1,4 @@
|
||||||
defmodule IgniterTest do
|
defmodule IgniterTest do
|
||||||
use ExUnit.Case
|
use ExUnit.Case
|
||||||
doctest Igniter
|
doctest Igniter
|
||||||
|
|
||||||
test "greets the world" do
|
|
||||||
assert Igniter.hello() == :world
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue