defmodule MishkaInstaller.Helper.Setting do
@moduledoc """
This module offers a number of necessary functions that can be used to access the runtime configuration.
The owner of the system has the ability to make changes to these parameters, which will then be updated in the database.
This section was formerly a part of `MishkaCMS`, and the accompanying system is likewise a very straightforward
one for saving particular settings when the system is being executed.
**Note**: that the functionality of this section is not restricted to the 'MishkaInstaller' section;
rather, it can be utilized in other parts of your product.
"""
use GenServer
require Logger
alias MishkaInstaller.Setting, as: DBSetting
@ets_table :setting_ets_state
@sync_with_database 100_000
@re_check_binding_db 10_000
@doc """
Start the ETS table `:setting_ets_state` and sync with the settings table in the Postgres database.
"""
def start_link(args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end
@doc """
Push your required configs to ETS.
"""
def push(config) do
ETS.Set.put!(table(), {String.to_atom(config.name), config.configs})
end
@doc """
Get your required configs from ETS.
"""
def get(config_name) do
case ETS.Set.get(table(), String.to_atom(config_name)) do
{:ok, {_name, config}} -> config
_ -> nil
end
end
@doc """
Get All your required configs from ETS.
"""
def get_all() do
ETS.Set.to_list!(table())
end
def delete(config_name) do
ETS.Set.delete(table(), String.to_atom(config_name))
end
@doc false
def child_spec(process_name) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, [process_name]},
restart: :transient,
max_restarts: 4
}
end
@impl true
def init(_state) do
Logger.info("The ETS state of settings was staretd")
Process.send_after(self(), :sync_with_database, @sync_with_database)
table =
ETS.Set.new!(
name: @ets_table,
protection: :public,
read_concurrency: true,
write_concurrency: true
)
{:ok, %{set: table}, {:continue, :sync_with_database}}
end
@impl true
def handle_continue(:sync_with_database, state) do
check_custom_pubsub_loaded(state)
end
@impl true
def handle_info(:timeout, state) do
Logger.info("We are waiting for your custom pubsub is loaded")
check_custom_pubsub_loaded(state)
end
@impl true
def handle_info(:sync_with_database, state) do
Logger.info("Setting ETS state was synced with database")
sync_with_database()
Process.send_after(self(), :sync_with_database, @sync_with_database)
{:noreply, state}
end
@impl true
def handle_info({:setting, :ok, action, repo_data}, state) do
Logger.warn("Your ETS state of setting is going to be updated")
case action do
:delete -> delete(repo_data.name)
_ -> push(repo_data)
end
{:noreply, state}
end
@impl true
def terminate(_reason, _state) do
Logger.warn("Your ETS state of setting was restarted by a problem")
end
@doc """
Sync the ETS config table with your database.
"""
def sync_with_database() do
DBSetting.settings()
|> Enum.map(&push/1)
end
defp check_custom_pubsub_loaded(state) do
custom_pubsub = MishkaInstaller.get_config(:pubsub)
custom_repo = MishkaInstaller.get_config(:repo)
cond do
is_nil(custom_pubsub) ->
if Mix.env() != :test do
raise "Please set a Phoenix PubSub module in your config based on MishkaInstaller document."
else
{:noreply, state, 100}
end
is_nil(Process.whereis(custom_pubsub)) ||
is_nil(Process.whereis(custom_repo)) ->
{:noreply, state, 100}
true ->
Process.send_after(self(), :sync_with_database, @re_check_binding_db)
MishkaInstaller.Setting.subscribe()
{:noreply, state}
end
end
defp table() do
case ETS.Set.wrap_existing(@ets_table) do
{:ok, set} ->
set
_ ->
start_link([])
table()
end
end
end