fix: configures?/3 -> configures_key & configures_root_key (#54)

This commit is contained in:
Igor Barakaiev 2024-07-19 15:54:41 +03:00 committed by GitHub
parent 3a272310c2
commit 8f9a94c449
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 220 additions and 44 deletions

View file

@ -453,7 +453,7 @@ defmodule Igniter.Code.Function do
end end
end end
else else
:error false
end end
end end

View file

@ -225,64 +225,163 @@ defmodule Igniter.Project.Config do
end end
end end
@doc "Returns `true` if the given configuration path is set somewhere after the provided zipper, or in the given configuration file." @doc """
@spec configures?(Igniter.t(), file :: String.t(), list(atom), atom()) :: boolean() Checks if either `config :root_key, _` or `config :root_key, _, _` is present
def configures?(igniter, file, path, app_name) do in the provided config file.
file_path = Path.join("config", file)
igniter = Note: The config file name should _not_ include the `config/` prefix.
Igniter.include_existing_elixir_file(igniter, file_path, required?: false) """
@spec configures_root_key?(Igniter.t(), String.t(), atom()) :: boolean()
case Rewrite.source(igniter.rewrite, file_path) do def configures_root_key?(igniter, config_file_name, root_key) do
{:ok, source} -> case load_config_zipper(igniter, config_file_name) do
source nil -> false
|> Rewrite.Source.get(:quoted) zipper -> configures_root_key?(zipper, root_key)
|> Zipper.zip()
|> configures?(path, app_name)
_ ->
false
end end
end end
@spec configures?(zipper :: Zipper.t(), list(atom), atom()) :: boolean() @doc """
def configures?(zipper, config_path, app_name) do Same as `configures_root_key?/3` but accepts a Zipper instead.
if Enum.count(config_path) == 1 do """
config_item = Enum.at(config_path, 0) @spec configures_root_key?(Zipper.t(), atom()) :: boolean()
def configures_root_key?(zipper = %Zipper{}, root_key) do
case Igniter.Code.Function.move_to_function_call_in_current_scope( with :error <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:config,
2,
&Igniter.Code.Function.argument_equals?(&1, 0, root_key)
),
:error <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper, zipper,
:config, :config,
3, 3,
fn function_call -> &Igniter.Code.Function.argument_equals?(&1, 0, root_key)
Igniter.Code.Function.argument_matches_pattern?(function_call, 0, ^app_name) &&
Igniter.Code.Function.argument_matches_pattern?(function_call, 1, ^config_item)
end
) do ) do
:error ->
false false
{:ok, _zipper} ->
true
end
else else
case Igniter.Code.Function.move_to_function_call_in_current_scope( {:ok, _zipper} -> true
end
end
@doc """
If the last argument is key, checks if either `config :root_key, :key, ...`
or `config :root_key, key: ...` is set.
If the last argument is a keyword path, checks if
`config :root_key, path_head, [...]` defines `path_rest` or if
`config :root_key, [...]` defines `path`, where `path_head` is the first
element of `path` and `path_rest` are the remaining elements.
Note: `config_file_name` should _not_ include the `config/` prefix.
"""
@spec configures_key?(
Igniter.t(),
String.t(),
atom(),
atom() | list(atom())
) :: boolean()
def configures_key?(igniter, config_file_name, root_key, key_or_path) do
case load_config_zipper(igniter, config_file_name) do
nil -> false
zipper -> configures_key?(zipper, root_key, key_or_path)
end
end
@doc """
Same as `configures_key?/4` but accepts a Zipper.
"""
@spec configures_key?(Zipper.t(), atom(), atom() | list(atom())) :: boolean()
def configures_key?(zipper, root_key, key_or_path)
def configures_key?(zipper = %Zipper{}, root_key, key) when is_atom(key) do
configures_key?(zipper, root_key, List.wrap(key))
end
def configures_key?(zipper = %Zipper{}, root_key, path) when is_list(path) do
with :error <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper, zipper,
:config, :config,
2, 2,
fn function_call -> fn function_call ->
Igniter.Code.Function.argument_matches_pattern?(function_call, 0, ^app_name) Igniter.Code.Function.argument_equals?(function_call, 0, root_key) and
Igniter.Code.Function.argument_matches_predicate?(
function_call,
1,
fn argument_zipper ->
Igniter.Code.Keyword.keyword_has_path?(argument_zipper, path)
end
)
end
),
:error <-
Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:config,
3,
fn function_call ->
case path do
[key] ->
Igniter.Code.Function.argument_equals?(function_call, 0, root_key) and
Igniter.Code.Function.argument_equals?(function_call, 1, key)
[path_head | path_rest] ->
Igniter.Code.Function.argument_equals?(function_call, 0, root_key) and
Igniter.Code.Function.argument_equals?(function_call, 1, path_head) and
Igniter.Code.Function.argument_matches_predicate?(
function_call,
2,
fn argument_zipper ->
Igniter.Code.Keyword.keyword_has_path?(argument_zipper, path_rest)
end
)
end
end end
) do ) do
:error -> false
:error else
{:ok, _zipper} -> true
{:ok, zipper} ->
Igniter.Code.Function.argument_matches_predicate?(zipper, 1, fn zipper ->
Igniter.Code.Keyword.keyword_has_path?(zipper, config_path)
end)
end end
end end
@doc "Returns `true` if the given configuration path is set somewhere after the provided zipper, or in the given configuration file."
@deprecated "Use configures_root_key?/3 or configures_key?/4 instead."
@spec configures?(Igniter.t(), String.t(), list(atom), atom()) :: boolean()
def configures?(igniter, config_file_name, path, app_name) do
case load_config_zipper(igniter, config_file_name) do
nil -> false
zipper -> configures?(zipper, path, app_name)
end
end
@spec configures?(Zipper.t(), list(atom), atom()) :: boolean()
@deprecated "Use configures_root_key?/2 or configures_key?/3 instead."
def configures?(zipper, path, app_name) do
case path do
[] ->
configures_root_key?(zipper, app_name)
path ->
configures_key?(zipper, app_name, path)
end
end
defp load_config_zipper(igniter, config_file_name) do
config_file_path = Path.join("config", config_file_name)
igniter =
Igniter.include_existing_elixir_file(igniter, config_file_path, required?: false)
case Rewrite.source(igniter.rewrite, config_file_path) do
{:ok, source} ->
source
|> Rewrite.Source.get(:quoted)
|> Zipper.zip()
_ ->
nil
end
end end
defp try_update_three_arg(zipper, config_path, app_name, value, updater) do defp try_update_three_arg(zipper, config_path, app_name, value, updater) do

View file

@ -83,7 +83,7 @@ defmodule Igniter.Project.ConfigTest do
""" """
end end
test "it choosees the thre 3 arg version when first item in path is not pretty" do test "it chooses the 3 arg version when first item in path is not pretty" do
%{rewrite: rewrite} = %{rewrite: rewrite} =
Igniter.new() Igniter.new()
|> Igniter.create_new_elixir_file("config/fake.exs", """ |> Igniter.create_new_elixir_file("config/fake.exs", """
@ -99,7 +99,7 @@ defmodule Igniter.Project.ConfigTest do
""" """
end end
test "it choosees the thre 3 arg version when first item in path is not pretty, and merges that way" do test "it chooses the 3 arg version when first item in path is not pretty, and merges that way" do
%{rewrite: rewrite} = %{rewrite: rewrite} =
Igniter.new() Igniter.new()
|> Igniter.create_new_elixir_file("config/fake.exs", """ |> Igniter.create_new_elixir_file("config/fake.exs", """
@ -280,4 +280,81 @@ defmodule Igniter.Project.ConfigTest do
""" """
end end
end end
test "configures_root_key?/3" do
igniter =
Igniter.new()
|> Igniter.create_new_elixir_file("config/fake.exs", """
import Config
config :foo, key1: "key1"
config Test, key2: "key2"
""")
assert Igniter.Project.Config.configures_root_key?(igniter, "fake.exs", :foo) == true
assert Igniter.Project.Config.configures_root_key?(igniter, "fake.exs", :fooo) == false
assert Igniter.Project.Config.configures_root_key?(igniter, "fake.exs", Test) == true
assert Igniter.Project.Config.configures_root_key?(igniter, "fake.exs", Testt) == false
end
describe "configures_key?/3" do
setup do
%{
igniter:
Igniter.new()
|> Igniter.create_new_elixir_file("config/fake.exs", """
import Config
config :foo, key1: [key2: "key2"]
config :bar, Test, key3: "key3"
config :xyz, Test, key4: [key5: "key5"]
""")
}
end
test "works when the last argument is a single atom and config/2 is used", %{igniter: igniter} do
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :foo, :key1) == true
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :foo, :key2) == false
end
test "works when the last argument is a single atom and config/3 is used", %{igniter: igniter} do
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :bar, Test) == true
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :bar, Testt) == false
end
test "works when the last argument is a path in a keyword list and config/2 is used", %{
igniter: igniter
} do
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :foo, [:key1, :key2]) ==
true
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :foo, [:key1, :key3]) ==
false
end
test "works when the last argument is a path in a keyword list and config/3 is used", %{
igniter: igniter
} do
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :bar, [Test, :key3]) ==
true
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :bar, [Test, :key4]) ==
false
# deeply nested
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :xyz, [
Test,
:key4,
:key5
]) ==
true
assert Igniter.Project.Config.configures_key?(igniter, "fake.exs", :xyz, [
Test,
:key4,
:key6
]) ==
false
end
end
end end