Packages
fnord
0.9.27
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
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.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
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.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/settings/frobs.ex
defmodule Settings.Frobs do
@moduledoc """
Manage frob enablement in settings.json using approvals-style arrays.
Schema:
- Global: ["frobs"] :: [string]
- Per-project: ["projects", pn, "frobs"] :: [string]
Effective enablement is the union of global and current project's frobs.
All mutations are performed via Settings APIs that provide cross-process locking
and atomic writes.
"""
@type scope :: :global | :project | {:project, String.t()}
@doc """
List enabled frobs for the given scope.
"""
@spec list(scope) :: [String.t()]
def list(:global) do
Settings.get(Settings.new(), "frobs", [])
|> sanitize_list()
end
def list(:project) do
case Settings.get_selected_project() do
{:ok, pn} -> list({:project, pn})
_ -> []
end
end
def list({:project, pn}) when is_binary(pn) do
settings = Settings.new()
project = Settings.get_project_data(settings, pn) || %{}
Map.get(project, "frobs", []) |> sanitize_list()
end
@doc """
Enable a frob in the given scope. Idempotent.
"""
@spec enable(scope, String.t()) :: :ok
def enable(scope, name) when is_binary(name) and byte_size(name) > 0 do
names =
list(scope)
|> List.wrap()
|> Kernel.++([name])
|> Enum.filter(&is_binary/1)
|> Enum.uniq()
|> Enum.sort()
set_list(scope, names)
end
@doc """
Disable a frob in the given scope. Idempotent.
"""
@spec disable(scope, String.t()) :: :ok
def disable(scope, name) when is_binary(name) do
names =
list(scope)
|> Enum.reject(&(&1 == name))
set_list(scope, names)
end
@doc """
Return the effective set of enabled frobs for the current project context
(union of global and project lists).
"""
@spec effective_enabled() :: MapSet.t(String.t())
def effective_enabled() do
global = list(:global)
project =
case Settings.get_selected_project() do
{:ok, _} -> list(:project)
_ -> []
end
MapSet.new(global ++ project)
end
@doc """
Is the given frob enabled (effective union)?
"""
@spec enabled?(String.t()) :: boolean()
def enabled?(name) when is_binary(name) do
MapSet.member?(effective_enabled(), name)
end
@doc """
Prune missing frobs from settings based on the given list of present frob names.
This removes any frob names that are not found in `present_names` from:
- the global `frobs` array, and
- the currently selected project's `frobs` array (if a project is selected)
Returns the list of names that were retained.
"""
@spec prune_missing!([String.t()]) :: [String.t()]
def prune_missing!(present_names) when is_list(present_names) do
set =
present_names
|> Enum.filter(&is_binary/1)
|> MapSet.new()
# Prune global list atomically
Settings.update(
Settings.new(),
"frobs",
fn current ->
current_list = if is_list(current), do: current, else: []
current_list
|> Enum.filter(&MapSet.member?(set, &1))
|> Enum.uniq()
|> Enum.sort()
end,
[]
)
# Prune currently selected project's list, if any
case Settings.get_selected_project() do
{:ok, pn} ->
settings = Settings.new()
project = Settings.get_project_data(settings, pn) || %{}
pruned =
project
|> Map.get("frobs", [])
|> Enum.filter(&MapSet.member?(set, &1))
|> Enum.uniq()
|> Enum.sort()
_ = Settings.set_project_data(settings, pn, Map.put(project, "frobs", pruned))
:ok
_ ->
:ok
end
# Return the list of names that were not removed (i.e., kept)
present_names
|> Enum.filter(&is_binary/1)
|> Enum.uniq()
|> Enum.sort()
end
# ----------------------------------------------------------------------------
# Internals
# ----------------------------------------------------------------------------
defp sanitize_list(list) when is_list(list) do
list
|> Enum.filter(&is_binary/1)
|> Enum.uniq()
|> Enum.sort()
end
defp sanitize_list(_), do: []
defp set_list(:global, names) when is_list(names) do
Settings.update(Settings.new(), "frobs", fn _ -> names end, [])
:ok
end
defp set_list(:project, names) when is_list(names) do
case Settings.get_selected_project() do
{:ok, pn} ->
settings = Settings.new()
project = Settings.get_project_data(settings, pn) || %{}
Settings.set_project_data(settings, pn, Map.put(project, "frobs", names))
:ok
_ ->
:ok
end
end
defp set_list({:project, pn}, names) when is_list(names) and is_binary(pn) do
settings = Settings.new()
project = Settings.get_project_data(settings, pn) || %{}
Settings.set_project_data(settings, pn, Map.put(project, "frobs", names))
:ok
end
end