Skip to content

Commit

Permalink
[#126] Add option :default_key_generator to change generator at com…
Browse files Browse the repository at this point in the history
…pile time
  • Loading branch information
cabol committed Jun 19, 2021
1 parent 36d3734 commit fbec1a5
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 26 deletions.
19 changes: 19 additions & 0 deletions lib/nebulex/cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ defmodule Nebulex.Cache do
@adapter adapter
@opts opts
@default_dynamic_cache opts[:default_dynamic_cache] || __MODULE__
@default_key_generator opts[:default_key_generator] || Nebulex.Caching.SimpleKeyGenerator
@before_compile adapter

## Config and metadata
Expand All @@ -303,6 +304,9 @@ defmodule Nebulex.Cache do
@impl true
def __adapter__, do: @adapter

@impl true
def __default_key_generator__, do: @default_key_generator

## Process lifecycle

@doc false
Expand Down Expand Up @@ -552,6 +556,21 @@ defmodule Nebulex.Cache do
"""
@callback __adapter__ :: Nebulex.Adapter.t()

@doc """
Returns the default key generator applied only when using
**"declarative annotation-based caching"** via `Nebulex.Caching`.
Sometimes you may want to set a different key generator when using
declarative caching. By default, the key generator is set to
`Nebulex.Caching.SimpleKeyGenerator`. You can change the default
key generator at compile time with:
use Nebulex.Cache, default_key_generator: MyKeyGenerator
See `Nebulex.Caching` and `Nebulex.Caching.KeyGenerator` for more information.
"""
@callback __default_key_generator__ :: Nebulex.Caching.KeyGenerator.t()

@doc """
Returns the adapter configuration stored in the `:otp_app` environment.
Expand Down
71 changes: 46 additions & 25 deletions lib/nebulex/caching.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,28 @@ if Code.ensure_loaded?(Decorator.Define) do
* If more than one param is given, return a key computed from the hashes
of all parameters (`:erlang.phash2(args)`).
See `Nebulex.Caching.SimpleKeyGenerator` for more information about the
default key generation.
The default key generator is provided by the cache via the callback
`c:Nebulex.Cache.__default_key_generator__/0` and it is applied only
if the option `key:` or `keys:` is not configured. By default it is
`Nebulex.Caching.SimpleKeyGenerator`. But you can change the default
key generator at compile time with the option `:default_key_generator`,
like so:
To provide a different default key generator, one needs to implement the
`Nebulex.Caching.KeyGenerator` behaviour. Then, it can be configured by
the option `:key_generator` for the desired declarations. For example:
defmodule MyApp.Cache do
use Nebulex.Cache,
otp_app: :my_app,
adapter: Nebulex.Adapters.Local,
default_key_generator: MyApp.Cache.CustomKeyGenerator
end
The given key generator `MyApp.Cache.CustomKeyGenerator` must implement
the `Nebulex.Caching.KeyGenerator` behaviour.
@decorate cache_put(cache: Cache, key_generator: MyKeyGenerator)
Also, you can provide a different key generator at any time
(overriding the default one) when using any caching annotation
through the option `:key_generator`. For example:
@decorate cache_put(cache: Cache, key_generator: MyApp.Cache.AnotherKeyGenerator)
def update_account(account) do
# the logic for updating the account ...
end
Expand Down Expand Up @@ -172,10 +186,9 @@ if Code.ensure_loaded?(Decorator.Define) do
* `:cache` - Defines what cache to use (required). Raises `ArgumentError`
if the option is not present.
* `:key` - Defines the cache access key (optional). If this option
is not present, a default key is generated by hashing the tuple
`{module, fun_name}`; the first element is the caller module and the
second one the function name (`:erlang.phash2({module, fun})`).
* `:key` - Defines the cache access key (optional). It overrides the
`:key_generator` option. If this option is not present, a default
key is generated by the configured or default key generator.
* `:opts` - Defines the cache options that will be passed as argument
to the invoked cache function (optional).
Expand All @@ -188,10 +201,11 @@ if Code.ensure_loaded?(Decorator.Define) do
be cached). Returning `false` will cause that nothing is stored in the
cache.
* `:key_generator` - The custom key generator module that implements the
`Nebulex.Caching.KeyGenerator` behaviour. In case `key:` or `keys:`
(in case of `cache_evict`) argument are not given,
`Nebulex.Caching.SimpleKeyGenerator` is used as default key generator.
* `:key_generator` - The custom key generator module implementing the
`Nebulex.Caching.KeyGenerator` behaviour. If present, this option
overrides the default key generator provided by the cache, and it is
applied only if the option `key:` or `keys:` is not configured.
In other words, the option `key:` or `keys:` overrides this option.
## Putting all together
Expand Down Expand Up @@ -383,7 +397,7 @@ if Code.ensure_loaded?(Decorator.Define) do
## Options
* `:keys` - Defines the set of keys to be evicted from cache on function
completion.
completion. It overrides the `:key_generator` option.
* `:all_entries` - Defines if all entries must be removed on function
completion. Defaults to `false`.
Expand Down Expand Up @@ -453,8 +467,6 @@ if Code.ensure_loaded?(Decorator.Define) do
end

defp keygen_block(attrs, ctx) do
keygen = attrs[:key_generator] || Nebulex.Caching.SimpleKeyGenerator

args =
for arg <- ctx.args do
case arg do
Expand All @@ -463,14 +475,23 @@ if Code.ensure_loaded?(Decorator.Define) do
end
end

if key = Keyword.get(attrs, :key) do
quote do
unquote(key)
end
else
quote do
unquote(keygen).generate(unquote(ctx.module), unquote(ctx.name), unquote(args))
end
cond do
key = Keyword.get(attrs, :key) ->
quote(do: unquote(key))

keygen = Keyword.get(attrs, :key_generator) ->
quote do
unquote(keygen).generate(unquote(ctx.module), unquote(ctx.name), unquote(args))
end

true ->
quote do
cache.__default_key_generator__().generate(
unquote(ctx.module),
unquote(ctx.name),
unquote(args)
)
end
end
end

Expand Down
3 changes: 3 additions & 0 deletions lib/nebulex/caching/key_generator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ defmodule Nebulex.Caching.KeyGenerator do
See the default implementation `Nebulex.Caching.SimpleKeyGenerator`.
"""

@typedoc "KeyGenerator"
@type t :: module

@doc """
Generates a key for the given `module`, `function_name`, and its `args`.
"""
Expand Down
11 changes: 10 additions & 1 deletion lib/nebulex/caching/simple_key_generator.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
defmodule Nebulex.Caching.SimpleKeyGenerator do
@moduledoc """
KeyGenerator implementation.
Default key generator implementation.
It implementats a simple algorithm:
* If no params are given, return `0`.
* If only one param is given, return that param as key.
* If more than one param is given, return a key computed from the hashes
of all parameters (`:erlang.phash2(args)`).
Based on the [default key generation in Spring Cache Abstraction](https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/cache.html#cache-annotations-cacheable-default-key).
"""
@behaviour Nebulex.Caching.KeyGenerator

Expand Down

0 comments on commit fbec1a5

Please sign in to comment.