Packages
localize
1.0.0-rc.2
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.41.3
0.41.2
0.41.1
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.1
0.30.0
retired
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
0.1.0-alpha.1
Localization (parsing, formatting) of numbers, dates/time/calendar, units of measure, messages and lists. Includes localized collation.
Current section
Files
Jump to
Current section
Files
lib/localize/data_loader.ex
defmodule Localize.DataLoader do
# A GenServer that serializes ETF data file loading to prevent
# race conditions.
#
# When multiple processes request the same data concurrently,
# the first request triggers the load and subsequent requests
# wait for it to complete. This avoids duplicate file reads and
# redundant `:persistent_term.put/2` calls (each of which
# triggers a global GC).
#
# The server is started as part of the `Localize` supervision
# tree. All ETF file loading goes through `load/2`.
#
@moduledoc false
use GenServer
# ── Public API ────────────────────────────────────────────────
@doc """
Starts the data loader GenServer.
"""
def start_link(options \\ []) do
GenServer.start_link(__MODULE__, options, name: __MODULE__)
end
@doc """
Loads an ETF data file, serialized through the GenServer.
If the data is already cached in `:persistent_term`, returns
it immediately without contacting the server. Otherwise,
delegates to the GenServer which ensures only one load per
key occurs.
### Arguments
* `key` is the `:persistent_term` key (e.g.,
`{:localize, :supplemental, "aliases.etf"}`).
* `load_fn` is a zero-arity function that reads and decodes
the file, returning the data to be cached.
### Returns
* The loaded data.
"""
@spec load(term(), (-> term())) :: term()
def load(key, load_fn) do
case :persistent_term.get(key, :not_loaded) do
:not_loaded ->
if server_running?() do
GenServer.call(__MODULE__, {:load, key, load_fn}, :infinity)
else
# Compile time or before app start — load directly
data = load_fn.()
:persistent_term.put(key, data)
data
end
data ->
data
end
end
defp server_running? do
pid = Process.whereis(__MODULE__)
pid && Process.alive?(pid)
end
# ── GenServer callbacks ───────────────────────────────────────
@impl GenServer
def init(_options) do
{:ok, %{}}
end
@impl GenServer
def handle_call({:load, key, load_fn}, _from, state) do
data =
case :persistent_term.get(key, :not_loaded) do
:not_loaded ->
data = load_fn.()
:persistent_term.put(key, data)
data
data ->
data
end
{:reply, data, state}
end
end