Packages
igniter
0.1.0
0.8.2
0.8.1
0.8.0
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.30
0.6.29
0.6.28
0.6.27
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.52
0.5.51
0.5.50
0.5.49
0.5.48
0.5.47
0.5.46
0.5.45
0.5.44
0.5.43
0.5.42
0.5.41
0.5.40
0.5.39
0.5.38
0.5.37
0.5.36
0.5.35
0.5.34
0.5.33
0.5.32
0.5.31
0.5.30
0.5.29
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.78
0.3.77
0.3.76
0.3.75
0.3.74
0.3.73
0.3.72
0.3.71
0.3.70
0.3.69
0.3.68
0.3.67
0.3.66
0.3.65
0.3.64
0.3.63
0.3.62
0.3.61
0.3.60
0.3.59
0.3.58
0.3.57
0.3.56
0.3.55
0.3.54
0.3.53
0.3.52
0.3.51
0.3.50
0.3.49
0.3.48
0.3.47
0.3.46
0.3.45
0.3.44
0.3.43
0.3.42
0.3.41
0.3.40
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A code generation and project patching framework
Current section
Files
Jump to
Current section
Files
lib/config.ex
defmodule Igniter.Config do
@moduledoc "Codemods and utilities for modifying Elixir config files."
require Igniter.Code.Function
alias Igniter.Code.Common
alias Sourceror.Zipper
@doc "Sets a config value in the given configuration file, if it is not already set."
@spec configure_new(Igniter.t(), Path.t(), atom(), list(atom), term()) :: Igniter.t()
def configure_new(igniter, file_path, app_name, config_path, value) do
configure(igniter, file_path, app_name, config_path, value, & &1)
end
@doc "Sets a config value in the given configuration file, updating it with `updater` if it is already set."
@spec configure(
Igniter.t(),
Path.t(),
atom(),
list(atom),
term(),
(Zipper.t() -> {:ok, Zipper.t()} | :error) | nil
) :: Igniter.t()
def configure(igniter, file_name, app_name, config_path, value, updater \\ nil) do
file_contents = "import Config\n"
file_path = Path.join("config", file_name)
config_path = List.wrap(config_path)
value =
case value do
{:code, value} -> value
value -> Macro.escape(value)
end
updater = updater || fn zipper -> {:ok, Zipper.replace(zipper, value)} end
igniter
|> ensure_default_configs_exist(file_name)
|> Igniter.include_or_create_elixir_file(file_path, file_contents)
|> Igniter.update_elixir_file(file_path, fn zipper ->
case Zipper.find(zipper, fn
{:import, _, [Config]} ->
true
{:import, _, [{:__aliases__, _, [:Config]}]} ->
true
_ ->
false
end) do
nil ->
{:error, "No call to `import Config` found in configuration file"}
zipper ->
modify_configuration_code(zipper, config_path, app_name, value, updater)
end
end)
end
defp ensure_default_configs_exist(igniter, "runtime.exs"), do: igniter
defp ensure_default_configs_exist(igniter, _file) do
igniter
|> Igniter.include_or_create_elixir_file("config/config.exs", """
import Config
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "\#{config_env()}.exs"
""")
|> Igniter.include_or_create_elixir_file("config/dev.exs", """
import Config
""")
|> Igniter.include_or_create_elixir_file("config/test.exs", """
import Config
""")
|> Igniter.include_or_create_elixir_file("config/prod.exs", """
import Config
""")
end
@doc """
Modifies elixir configuration code starting at the configured zipper.
If you want to set configuration, use `configure/6` or `configure_new/5` instead. This is a lower-level
tool for modifying configuration files when you need to adjust some specific part of them.
"""
@spec modify_configuration_code(
Zipper.t(),
list(atom),
atom(),
term(),
(Zipper.t() -> {:ok, Zipper.t()} | :error) | nil
) :: Igniter.t()
def modify_configuration_code(zipper, config_path, app_name, value, updater \\ nil) do
updater = updater || fn zipper -> {:ok, Zipper.replace(zipper, value)} end
case try_update_three_arg(zipper, config_path, app_name, value, updater) do
{:ok, zipper} ->
zipper
:error ->
case try_update_two_arg(zipper, config_path, app_name, value, updater) do
{:ok, zipper} ->
zipper
:error ->
[first | rest] = config_path
# this indicates its a module / not a "pretty" atom
config =
if is_atom(first) && String.downcase(to_string(first)) != to_string(first) do
{:config, [], [app_name, first, Igniter.Code.Keyword.keywordify(rest, value)]}
else
{:config, [], [app_name, [{first, Igniter.Code.Keyword.keywordify(rest, value)}]]}
end
case Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:import,
1,
fn function_call ->
Igniter.Code.Function.argument_matches_predicate?(
function_call,
0,
&Common.nodes_equal?(&1, Config)
)
end
) do
{:ok, zipper} ->
zipper
|> Zipper.right()
|> case do
nil ->
Common.add_code(zipper, config)
zipper ->
Common.add_code(zipper, config, :before)
end
end
end
end
end
@doc "Returns `true` if the given configuration path is set somewhere after the provided zipper."
@spec configures?(Zipper.t(), list(atom), atom()) :: boolean()
def configures?(zipper, config_path, app_name) do
if Enum.count(config_path) == 1 do
config_item = Enum.at(config_path, 0)
case Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:config,
3,
fn function_call ->
Igniter.Code.Function.argument_matches_pattern?(function_call, 0, ^app_name) &&
Igniter.Code.Function.argument_matches_pattern?(function_call, 1, ^config_item)
end
) do
:error ->
false
{:ok, _zipper} ->
true
end
else
case Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:config,
2,
fn function_call ->
Igniter.Code.Function.argument_matches_pattern?(function_call, 0, ^app_name)
end
) do
:error ->
:error
{: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
defp try_update_three_arg(zipper, config_path, app_name, value, updater) do
if Enum.count(config_path) == 1 do
config_item = Enum.at(config_path, 0)
case Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:config,
3,
fn function_call ->
Igniter.Code.Function.argument_matches_pattern?(function_call, 0, ^app_name) &&
Igniter.Code.Function.argument_matches_pattern?(function_call, 1, ^config_item)
end
) do
:error ->
:error
{:ok, zipper} ->
Igniter.Code.Function.update_nth_argument(zipper, 2, updater)
end
else
[config_item | path] = config_path
case Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:config,
3,
fn function_call ->
Igniter.Code.Function.argument_matches_pattern?(function_call, 0, ^app_name) &&
(Igniter.Code.Function.argument_matches_pattern?(function_call, 1, ^config_item) ||
Igniter.Code.Function.argument_matches_predicate?(
function_call,
1,
&Common.nodes_equal?(&1, config_item)
))
end
) do
:error ->
:error
{:ok, zipper} ->
with {:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 2),
{:ok, zipper} <- Igniter.Code.Keyword.put_in_keyword(zipper, path, value, updater) do
{:ok, zipper}
else
_ ->
:error
end
end
end
end
defp try_update_two_arg(zipper, config_path, app_name, value, updater) do
case Igniter.Code.Function.move_to_function_call_in_current_scope(
zipper,
:config,
2,
fn function_call ->
Igniter.Code.Function.argument_matches_pattern?(function_call, 0, ^app_name)
end
) do
:error ->
:error
{:ok, zipper} ->
Igniter.Code.Function.update_nth_argument(
zipper,
1,
&Igniter.Code.Keyword.put_in_keyword(&1, config_path, value, updater)
)
end
end
end