Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.3.13
- fix: allow `Cache.ConCache` to accept `ets_options` (strict NimbleOptions validation + normalization)
- feat: allow `Cache.ETS` `write_concurrency: :auto` (OTP 25+)

# 0.3.12
- chore: fix warnings

Expand Down
2 changes: 1 addition & 1 deletion lib/cache/con_cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ defmodule Cache.ConCache do
:named_table
| :compressed
| {:heir, pid()}
| {:write_concurrency, boolean()}
| {:write_concurrency, boolean() | :auto}
| {:read_concurrency, boolean()}
| :ordered_set
| :set
Expand Down
47 changes: 46 additions & 1 deletion lib/cache/ets.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Cache.ETS do
@opts_definition [
write_concurrency: [
type: :boolean,
type: {:or, [:boolean, {:in, [:auto]}]},
doc: "Enable write concurrency"
],
read_concurrency: [
Expand Down Expand Up @@ -225,6 +225,51 @@ defmodule Cache.ETS do
@impl Cache
def opts_definition, do: @opts_definition

def opts_definition(opts) when is_list(opts) do
if Keyword.keyword?(opts) do
validate_and_normalize_ets_options(opts)
else
{:error, "expected a keyword list"}
end
end

def opts_definition(_opts), do: {:error, "expected a keyword list"}

defp validate_and_normalize_ets_options(opts) do
case NimbleOptions.validate(opts, @opts_definition) do
{:ok, validated} ->
{:ok, normalize_ets_options(opts, validated)}

{:error, %NimbleOptions.ValidationError{} = error} ->
{:error, Exception.message(error)}
end
end

defp normalize_ets_options(original_opts, validated) do
Enum.reject(
[
maybe_type(original_opts, validated),
maybe_compressed(original_opts, validated),
maybe_kv(original_opts, validated, :read_concurrency),
maybe_kv(original_opts, validated, :write_concurrency),
maybe_kv(original_opts, validated, :decentralized_counters)
],
&is_nil/1
)
end

defp maybe_type(original_opts, validated) do
if Keyword.has_key?(original_opts, :type), do: validated[:type]
end

defp maybe_compressed(original_opts, validated) do
if Keyword.has_key?(original_opts, :compressed) and validated[:compressed], do: :compressed
end

defp maybe_kv(original_opts, validated, key) do
if Keyword.has_key?(original_opts, key), do: {key, validated[key]}
end

@impl Cache
def start_link(opts) do
Task.start_link(fn ->
Expand Down
4 changes: 1 addition & 3 deletions lib/cache/sandbox.ex
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,7 @@ defmodule Cache.Sandbox do
end)
end

def insert_raw(cache_name, data) when is_tuple(data) do
{key, value} = data

def insert_raw(cache_name, {key, value}) do
Agent.update(cache_name, fn state ->
Map.put(state, key, value)
end)
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ElixirCache.MixProject do
def project do
[
app: :elixir_cache,
version: "0.3.12",
version: "0.3.13",
elixir: "~> 1.11",
start_permanent: Mix.env() == :prod,
description:
Expand Down
33 changes: 33 additions & 0 deletions test/cache/con_cache_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,39 @@ defmodule Cache.ConCacheTest do
"""
use ExUnit.Case, async: true

describe "ets_options validation" do
test "accepts ets_options keyword list" do
validated =
NimbleOptions.validate!(
[ets_options: [read_concurrency: true, write_concurrency: :auto]],
Cache.ConCache.opts_definition()
)

assert [
{:read_concurrency, true},
{:write_concurrency, :auto}
] = validated[:ets_options]
end

test "rejects unknown ets_options keys" do
assert_raise NimbleOptions.ValidationError, fn ->
NimbleOptions.validate!(
[ets_options: [read_concurency: true]],
Cache.ConCache.opts_definition()
)
end
end

test "rejects invalid ets_options value types" do
assert_raise NimbleOptions.ValidationError, fn ->
NimbleOptions.validate!(
[ets_options: [read_concurrency: :yes]],
Cache.ConCache.opts_definition()
)
end
end
end

defmodule ConCacheAdapter do
use Cache,
adapter: Cache.ConCache,
Expand Down