Packages
potionx
0.2.15
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.1.1
0.1.0
Potionx is a set of generators and functions that speeds up the process of setting up and deploying a full-stack application that uses Elixir with GraphQL for the server-side component and Vue for the frontend component.
Current section
Files
Jump to
Current section
Files
lib/potionx/pow/pow_redis_cache.ex
defmodule Potionx.Pow.RedisCache do
@behaviour Pow.Store.Backend.Base
alias Pow.Config
@redix_instance_name :redix
@impl true
def put(config, record_or_records) do
ttl = Config.get(config, :ttl) || raise_ttl_error!()
commands =
record_or_records
|> List.wrap()
|> Enum.map(fn {key, value} ->
config
|> binary_redis_key(key)
|> put_command(value, ttl)
end)
Task.start(fn ->
@redix_instance_name
|> Redix.pipeline!(commands)
|> Enum.map(fn
"OK" -> nil
error -> error
end)
|> Enum.reject(&is_nil/1)
|> case do
[] -> :ok
errors -> raise "Redix failed SET because of #{inspect errors}"
end
end)
:ok
end
defp put_command(key, value, ttl) do
value = :erlang.term_to_binary(value)
["SET", key, value, "PX", ttl]
end
@impl true
def delete(config, key) do
key =
config
|> redis_key(key)
|> to_binary_redis_key()
Task.start(fn ->
Redix.command!(@redix_instance_name, ["DEL", key])
end)
:ok
end
@impl true
def get(config, key) do
key =
config
|> redis_key(key)
|> to_binary_redis_key()
case Redix.command!(@redix_instance_name, ["GET", key]) do
nil -> :not_found
value -> :erlang.binary_to_term(value)
end
end
@impl true
def all(config, key_match) do
compiled_match_spec = :ets.match_spec_compile([{{key_match, :_}, [], [:"$_"]}])
Stream.resource(
fn -> do_scan(config, compiled_match_spec, "0") end,
&stream_scan(config, compiled_match_spec, &1),
fn _ -> :ok end)
|> Enum.to_list()
end
defp stream_scan(_config, _compiled_match_spec, {[], "0"}), do: {:halt, nil}
defp stream_scan(config, compiled_match_spec, {[], iterator}) do
result = do_scan(config, compiled_match_spec, iterator)
stream_scan(config, compiled_match_spec, result)
end
defp stream_scan(_config, _compiled_match_spec, {keys, iterator}), do: {keys, {[], iterator}}
defp do_scan(config, compiled_match_spec, iterator) do
prefix = to_binary_redis_key([namespace(config)]) <> ":*"
[iterator, res] = Redix.command!(@redix_instance_name, ["SCAN", iterator, "MATCH", prefix])
{filter_or_load_value(compiled_match_spec, res, config), iterator}
end
defp filter_or_load_value(compiled_match_spec, keys, config) do
keys
|> Enum.map(&convert_key/1)
|> Enum.sort()
|> :ets.match_spec_run(compiled_match_spec)
|> populate_values(config)
end
defp convert_key(key) do
key =
key
|> from_binary_redis_key()
|> unwrap()
{key, nil}
end
defp unwrap([_namespace, key]), do: key
defp unwrap([_namespace | key]), do: key
defp populate_values([], _config), do: []
defp populate_values(records, config) do
binary_keys = Enum.map(records, fn {key, nil} -> binary_redis_key(config, key) end)
values =
@redix_instance_name
|> Redix.command!(["MGET"] ++ binary_keys)
|> Enum.map(&:erlang.binary_to_term/1)
records
|> zip_values(values)
|> Enum.reject(fn {_key, value} -> is_nil(value) end)
end
defp zip_values([{key, nil} | next1], [value | next2]) do
[{key, value} | zip_values(next1, next2)]
end
defp zip_values(_, []), do: []
defp zip_values([], _), do: []
defp binary_redis_key(config, key) do
config
|> redis_key(key)
|> to_binary_redis_key()
end
defp redis_key(config, key) do
[namespace(config) | List.wrap(key)]
end
defp namespace(config), do: Config.get(config, :namespace, "cache")
defp to_binary_redis_key(key) do
key
|> Enum.map(fn part ->
part
|> :erlang.term_to_binary()
|> Base.url_encode64(padding: false)
end)
|> Enum.join(":")
end
defp from_binary_redis_key(key) do
key
|> String.split(":")
|> Enum.map(fn part ->
part
|> Base.url_decode64!(padding: false)
|> :erlang.binary_to_term()
end)
end
@spec raise_ttl_error! :: no_return()
defp raise_ttl_error!,
do: Config.raise_error("`:ttl` configuration option is required for #{inspect(__MODULE__)}")
end