Current section
Files
Jump to
Current section
Files
lib/cache.ex
# lib/cache.ex - Add enabled check
defmodule Carve.Cache do
@moduledoc """
Request-scoped cache for Carve to prevent redundant data fetching within a single render.
Each render operation gets its own isolated cache keyed by a unique ref.
TTL defaults to 500ms. Increase if requests involve slow queries.
The cache is cleared at end of each render regardless of TTL.
Configuration:
config :carve, Carve.Config,
enable_cache: false,
cache_ttl: 100
"""
@doc """
Creates a new cache context for a render operation.
Returns a cache key that should be passed through the render pipeline.
"""
def new_context do
if Carve.Config.caching_enabled?() do
cache_key = make_ref()
Cachex.put(:carve_cache, cache_key, %{}, ttl: Carve.Config.cache_ttl())
cache_key
else
nil
end
end
@doc """
Fetches a value from cache or executes the function if not cached.
If caching is disabled, always executes the function.
"""
def fetch(nil, _key, fun) when is_function(fun, 0), do: fun.()
def fetch(cache_key, key, fun) when is_function(fun, 0) do
if Carve.Config.caching_enabled?() do
cache_data =
case Cachex.get(:carve_cache, cache_key) do
{:ok, nil} -> %{}
{:ok, data} -> data
{:error, _} -> %{}
end
case Map.get(cache_data, key) do
nil ->
value = fun.()
# Re-read cache to pick up writes from nested fetch calls
current_cache =
case Cachex.get(:carve_cache, cache_key) do
{:ok, nil} -> %{}
{:ok, data} -> data
{:error, _} -> %{}
end
updated_cache = Map.put(current_cache, key, value)
Cachex.put(:carve_cache, cache_key, updated_cache, ttl: Carve.Config.cache_ttl())
value
cached_value ->
cached_value
end
else
fun.()
end
end
@doc """
Gets the current cache key from the process dictionary, or creates a new one.
Returns nil if caching is disabled.
"""
def get_or_create_context do
if Carve.Config.caching_enabled?() do
case Process.get(:carve_cache_key) do
nil ->
cache_key = new_context()
Process.put(:carve_cache_key, cache_key)
cache_key
cache_key ->
cache_key
end
else
nil
end
end
@doc """
Clears the cache context from the process dictionary.
"""
def clear_context do
if Carve.Config.caching_enabled?() do
Process.delete(:carve_cache_key)
end
end
end