Created
May 13, 2018 07:53
-
-
Save fertapric/427505e866a16c5073cee086683a13b3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Params do | |
@moduledoc """ | |
A set of functions for working with parameters. | |
Parameters are maps that impose restrictions on the key type: | |
* all the keys must be atoms or strings. | |
* all the keys must be of the same type. | |
Otherwise, all the functions of this module will raise `Params.MixedKeysError`. | |
""" | |
@type t :: %{required(String.t()) => any} | %{required(atom) => any} | |
@type key :: atom | String.t() | |
defguard is_key(value) when is_binary(value) or is_atom(value) | |
defmodule MixedKeysError do | |
defexception [:message, :params] | |
def exception(params) do | |
%MixedKeysError{ | |
message: | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: #{inspect(params)}", | |
params: params | |
} | |
end | |
end | |
@doc """ | |
Deletes the entry in `params` for a specific `key`. | |
If `:key` or `"key"` does not exist, returns `params` unchanged. | |
## Examples | |
iex> Params.delete(%{a: 1, b: 2}, :a) | |
%{b: 2} | |
iex> Params.delete(%{a: 1, b: 2}, "a") | |
%{b: 2} | |
iex> Params.delete(%{"a" => 1, "b" => 2}, :a) | |
%{"b" => 2} | |
iex> Params.delete(%{"a" => 1, "b" => 2}, "a") | |
%{"b" => 2} | |
iex> Params.delete(%{b: 2}, :a) | |
%{b: 2} | |
""" | |
@spec delete(t, key) :: t | |
def delete(%{} = params, key) when is_key(key) do | |
cond do | |
all_atoms?(params) -> Map.delete(params, :"#{key}") | |
all_strings?(params) -> Map.delete(params, "#{key}") | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Drops the given `keys` from `params`. | |
If `keys` contains keys (`:key` or `"key"`) that are not in `params`, | |
they're simply ignored. | |
## Examples | |
iex> Params.drop(%{a: 1, b: 2, c: 3}, [:b, "c", "d"]) | |
%{a: 1} | |
iex> Params.drop(%{"a" => 1, "b" => 2, "c" => 3}, [:b, :c, "d"]) | |
%{"a" => 1} | |
""" | |
@spec drop(t, [key]) :: t | |
def drop(%{} = params, keys) when is_list(keys), do: do_drop(params, keys) | |
def drop(%{} = params, %MapSet{} = keys), do: do_drop(params, keys) | |
defp do_drop(%{} = params, keys), do: Enum.reduce(keys, params, &delete(&2, &1)) | |
@doc """ | |
Checks if two params are equivalent. | |
Two params are considered to be equivalent if they contain equivalent | |
keys (`:key ~ "key"`) and those keys contain the same values. | |
## Examples | |
iex> Params.equivalent?(%{a: 1, b: 2}, %{b: 2, a: 1}) | |
true | |
iex> Params.equivalent?(%{a: 1, b: 2}, %{"b" => 2, "a" => 1}) | |
true | |
iex> Params.equivalent?(%{a: 1, b: 2}, %{b: 1, a: 2}) | |
false | |
iex> Params.equivalent?(%{"a" => 1, "b" => 2}, %{b: 1, a: 2}) | |
false | |
""" | |
@spec equivalent?(t, t) :: boolean | |
def equivalent?(%{} = params1, %{} = params2) do | |
equivalent_keys?(params1, params2) and equivalent_keys?(params2, params1) | |
end | |
defp equivalent_keys?(params1, params2) do | |
unless all_atoms?(params1) or all_strings?(params1), do: raise(MixedKeysError, params1) | |
Enum.all?(params1, fn {key, value} -> | |
case fetch(params2, key) do | |
{:ok, ^value} -> true | |
_ -> false | |
end | |
end) | |
end | |
@doc """ | |
Fetches the value for a specific `key` in the given `params`. | |
If `params` contains `:key` or `"key"` with value `value`, then | |
`{:ok, value}` is returned. If `params` doesn’t contain `key`, | |
`:error` is returned. | |
## Examples | |
iex> Params.fetch(%{a: 1, b: 2}, :a) | |
{:ok, 1} | |
iex> Params.fetch(%{a: 1, b: 2}, "a") | |
{:ok, 1} | |
iex> Params.fetch(%{"a" => 1, "b" => 2}, :a) | |
{:ok, 1} | |
iex> Params.fetch(%{"a" => 1, "b" => 2}, "a") | |
{:ok, 1} | |
iex> Params.fetch(%{a: 1, b: 2}, :c) | |
:error | |
""" | |
@spec fetch(t, key) :: {:ok, any} | :error | |
def fetch(%{} = params, key) when is_key(key) do | |
cond do | |
all_atoms?(params) -> Map.fetch(params, :"#{key}") | |
all_strings?(params) -> Map.fetch(params, "#{key}") | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Fetches the value for a specific `key` in the given `params`, | |
erroring out if `params` doesn’t contain `key`. | |
If `params` contains `:key` or `"key"`, the corresponding value is returned. | |
If `params` doesn’t contain `key`, a `KeyError` exception is raised. | |
## Examples | |
iex> Params.fetch!(%{a: 1, b: 2}, :a) | |
1 | |
iex> Params.fetch!(%{a: 1, b: 2}, "a") | |
1 | |
iex> Params.fetch!(%{"a" => 1, "b" => 2}, :a) | |
1 | |
iex> Params.fetch!(%{"a" => 1, "b" => 2}, "a") | |
1 | |
iex> Params.fetch!(%{a: 1, b: 2}, :c) | |
** (KeyError) key :c not found in: %{a: 1, b: 2} | |
""" | |
@spec fetch!(t, key) :: any | no_return | |
def fetch!(%{} = params, key) when is_key(key) do | |
cond do | |
all_atoms?(params) -> Map.fetch!(params, :"#{key}") | |
all_strings?(params) -> Map.fetch!(params, "#{key}") | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Filters `params`, i.e. returns only those elements for which `fun` returns | |
a truthy value. | |
See also `reject/2` which discards all elements where the function returns true. | |
## Examples | |
iex> Params.filter(%{a: 1, b: nil}, fn _k, v -> not is_nil(v) end) | |
%{a: 1} | |
iex> Params.filter(%{"a": 1, "b": nil}, fn _k, v -> not is_nil(v) end) | |
%{"a": 1} | |
""" | |
@spec filter(t, (key, any -> as_boolean(term))) :: t | |
def filter(%{} = params, fun) when is_function(fun, 2) do | |
if all_atoms?(params) or all_strings?(params) do | |
params | |
|> Enum.filter(fn {key, value} -> fun.(key, value) end) | |
|> Enum.into(%{}) | |
else | |
raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Gets the value for a specific `key` in `params`. | |
If `:key` or `"key"` is present in `params` with value `value`, | |
then `value` is returned. Otherwise, `default` is returned (which | |
is `nil` unless specified otherwise). | |
## Examples | |
iex> Params.get(%{}, :a) | |
nil | |
iex> Params.get(%{a: 1}, :a) | |
1 | |
iex> Params.get(%{a: 1}, "a") | |
1 | |
iex> Params.get(%{"a" => 1}, :a) | |
1 | |
iex> Params.get(%{"a" => 1}, "a") | |
1 | |
iex> Params.get(%{a: 1}, :b) | |
nil | |
iex> Params.get(%{a: 1}, :b, 3) | |
3 | |
""" | |
@spec get(t, key, any) :: any | |
def get(%{} = params, key, default \\ nil) when is_key(key) do | |
cond do | |
all_atoms?(params) -> Map.get(params, :"#{key}", default) | |
all_strings?(params) -> Map.get(params, "#{key}", default) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Gets the value from `key` and updates it, all in one pass. | |
`fun` is called with the current value under `key` in `params` (or `nil` | |
if `:key` or `"key"` are not present in `params`) and must return a | |
two-element tuple: the “get” value (the retrieved value, which can be | |
operated on before being returned) and the new value to be stored under | |
`key` in the resulting new params. `fun` may also return `:pop`, which | |
means the current value shall be removed from `params` and returned | |
(making this function behave like `Params.pop(params, key)`). | |
The returned value is a tuple with the “get” value returned by `fun` and | |
a new map with the updated value under `key`. | |
## Examples | |
iex> Params.get_and_update(%{a: 1}, :a, fn current_value -> | |
...> {current_value, "new value!"} | |
...> end) | |
{1, %{a: "new value!"}} | |
iex> Params.get_and_update(%{a: 1}, "a", &{&1, 2}) | |
{1, %{a: 2}} | |
iex> Params.get_and_update(%{"a" => 1}, :a, &{&1, 2}) | |
{1, %{"a" => 2}} | |
iex> Params.get_and_update(%{"a" => 1}, :a, &{&1, 2}) | |
{1, %{"a" => 2}} | |
iex> Params.get_and_update(%{a: 1}, :b, fn current_value -> | |
...> {current_value, "new value!"} | |
...> end) | |
{nil, %{b: "new value!", a: 1}} | |
iex> Params.get_and_update(%{a: 1}, "b", &{&1, 2}) | |
{nil, %{a: 1, b: 2}} | |
iex> Params.get_and_update(%{"a" => 1}, :b, &{&1, 2}) | |
{nil, %{"a" => 1, "b" => 2}} | |
iex> Params.get_and_update(%{"a" => 1}, :b, &{&1, 2}) | |
{nil, %{"a" => 1, "b" => 2}} | |
iex> Params.get_and_update(%{a: 1}, :a, fn _ -> :pop end) | |
{1, %{}} | |
iex> Params.get_and_update(%{a: 1}, :b, fn _ -> :pop end) | |
{nil, %{a: 1}} | |
""" | |
@spec get_and_update(t, key, (any -> {any, any} | :pop)) :: {any, t} | |
def get_and_update(%{} = params, key, fun) when is_key(key) and is_function(fun, 1) do | |
cond do | |
all_atoms?(params) -> Map.get_and_update(params, :"#{key}", fun) | |
all_strings?(params) -> Map.get_and_update(params, "#{key}", fun) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Gets the value from `key` and updates it. Raises if there is no `:key` | |
nor `"key"`. | |
Behaves exactly like `get_and_update/3`, but raises a `KeyError` exception | |
if `:key` nor `"key"` are not present in `params`. | |
## Examples | |
iex> Params.get_and_update!(%{a: 1}, :a, fn current_value -> | |
...> {current_value, "new value!"} | |
...> end) | |
{1, %{a: "new value!"}} | |
iex> Params.get_and_update!(%{a: 1}, "a", &{&1, 2}) | |
{1, %{a: 2}} | |
iex> Params.get_and_update!(%{"a" => 1}, :a, &{&1, 2}) | |
{1, %{"a" => 2}} | |
iex> Params.get_and_update!(%{"a" => 1}, "a", &{&1, 2}) | |
{1, %{"a" => 2}} | |
iex> Params.get_and_update!(%{a: 1}, :b, fn current_value -> | |
...> {current_value, "new value!"} | |
...> end) | |
** (KeyError) key :b not found in: %{a: 1} | |
iex> Params.get_and_update!(%{a: 1}, :a, fn _ -> | |
...> :pop | |
...> end) | |
{1, %{}} | |
""" | |
@spec get_and_update!(t, key, (any -> {any, any} | :pop)) :: any | |
def get_and_update!(%{} = params, key, fun) when is_key(key) and is_function(fun, 1) do | |
cond do | |
all_atoms?(params) -> Map.get_and_update!(params, :"#{key}", fun) | |
all_strings?(params) -> Map.get_and_update!(params, "#{key}", fun) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Gets the value for a specific `key` in `params`. | |
If `:key` or `"key"` are present in `params` with value `value`, | |
then value is returned. Otherwise, `fun` is evaluated and its result | |
is returned. | |
This is useful if the default value is very expensive to calculate or | |
generally difficult to setup and teardown again. | |
## Examples | |
iex> Params.get_lazy(%{a: 1}, :a, fn -> | |
...> # some expensive operation here | |
...> 13 | |
...> end) | |
1 | |
iex> Params.get_lazy(%{a: 1}, "a", fn -> 13 end) | |
1 | |
iex> Params.get_lazy(%{"a" => 1}, :a, fn -> 13 end) | |
1 | |
iex> Params.get_lazy(%{"a" => 1}, "a", fn -> 13 end) | |
1 | |
iex> Params.get_lazy(%{a: 1}, :b, fn -> 13 end) | |
13 | |
""" | |
@spec get_lazy(t, key, (() -> any)) :: any | |
def get_lazy(%{} = params, key, fun) when is_key(key) and is_function(fun, 0) do | |
cond do | |
all_atoms?(params) -> Map.get_lazy(params, :"#{key}", fun) | |
all_strings?(params) -> Map.get_lazy(params, "#{key}", fun) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Returns whether `:key` or `"key"` exists in the given `params`. | |
## Examples | |
iex> Params.has_key?(%{a: 1}, :a) | |
true | |
iex> Params.has_key?(%{a: 1}, "a") | |
true | |
iex> Params.has_key?(%{"a" => 1}, :a) | |
true | |
iex> Params.has_key?(%{"a" => 1}, "a") | |
true | |
iex> Params.has_key?(%{a: 1}, :b) | |
false | |
""" | |
@spec has_key?(t, key) :: boolean | |
def has_key?(%{} = params, key) when is_key(key) do | |
cond do | |
all_atoms?(params) -> Map.has_key?(params, :"#{key}") | |
all_strings?(params) -> Map.has_key?(params, "#{key}") | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Returns whether all `keys` exist in the given `params`. | |
## Examples | |
iex> Params.has_keys?(%{a: 1, b: 2}, [:a, "b"]) | |
true | |
iex> Params.has_keys?(%{"a" => 1, "b" => 2}, [:a, "b"]) | |
true | |
iex> Params.has_keys?(%{a: 1}, [:a, "b"]) | |
false | |
""" | |
@spec has_keys?(t, [key]) :: boolean | |
def has_keys?(%{} = params, keys) when is_list(keys), do: do_has_keys?(params, keys) | |
def has_keys?(%{} = params, %MapSet{} = keys), do: do_has_keys?(params, keys) | |
defp do_has_keys?(%{} = params, keys), do: Enum.all?(keys, &has_key?(params, &1)) | |
@doc """ | |
Merges two params into one. | |
All keys in `params2` will be added to `params1`, overriding any existing | |
one (i.e., the keys in `params2` "have precedence" over the ones in `params1`). | |
## Examples | |
iex> Params.merge(%{a: 1, b: 2}, %{a: 3, d: 4}) | |
%{a: 3, b: 2, d: 4} | |
iex> Params.merge(%{a: 1, b: 2}, %{"a" => 3, "d" => 4}) | |
%{a: 3, b: 2, d: 4} | |
iex> Params.merge(%{"a" => 1, "b" => 2}, %{a: 3, d: 4}) | |
%{"a" => 3, "b" => 2, "d" => 4} | |
iex> Params.merge(%{"a" => 1, "b" => 2}, %{"a" => 3, "d" => 4}) | |
%{"a" => 3, "b" => 2, "d" => 4} | |
""" | |
@spec merge(t, t) :: t | |
def merge(%{} = params1, %{} = params2) do | |
unless all_atoms?(params2) or all_strings?(params2), do: raise(MixedKeysError, params2) | |
cond do | |
all_atoms?(params1) -> | |
Enum.reduce(params2, params1, fn {key, value}, params1 -> | |
Map.put(params1, :"#{key}", value) | |
end) | |
all_strings?(params1) -> | |
Enum.reduce(params2, params1, fn {key, value}, params1 -> | |
Map.put(params1, "#{key}", value) | |
end) | |
true -> | |
raise MixedKeysError, params1 | |
end | |
end | |
@doc """ | |
Merges two params into one, resolving conflicts through the given `fun`. | |
All keys in `params2` will be added to `params1`. The given function will | |
be invoked when there are duplicate keys; its arguments are `key` (the | |
duplicate key), `value1` (the value of `key` in `params1`), and `value2` | |
(the value of `key` in `params2`). The value returned by `fun` is used as | |
the value under `key` in the resulting params. | |
## Examples | |
iex> Params.merge(%{a: 1, b: 2}, %{"a" => 3, "d" => 4}, fn :a, v1, v2 -> | |
...> v1 + v2 | |
...> end) | |
%{a: 4, b: 2, d: 4} | |
iex> Params.merge(%{"a" => 1, "b" => 2}, %{a: 3, d: 4}, fn "a", v1, v2 -> | |
...> v1 + v2 | |
...> end) | |
%{"a" => 4, "b" => 2, "d" => 4} | |
""" | |
@spec merge(t, t, (key, any, any -> any)) :: t | |
def merge(%{} = params1, %{} = params2, fun) when is_function(fun, 3) do | |
unless all_atoms?(params2) or all_strings?(params2), do: raise(MixedKeysError, params2) | |
key_fun = | |
cond do | |
all_atoms?(params1) -> fn key -> :"#{key}" end | |
all_strings?(params1) -> fn key -> "#{key}" end | |
true -> raise MixedKeysError, params1 | |
end | |
Enum.reduce(params2, params1, fn {key, value2}, params1 -> | |
param_key = key_fun.(key) | |
case Map.fetch(params1, param_key) do | |
{:ok, value1} -> Map.put(params1, param_key, fun.(param_key, value1, value2)) | |
:error -> Map.put(params1, param_key, value2) | |
end | |
end) | |
end | |
@doc """ | |
Returns and removes the value associated with `key` in `params`. | |
If `:key` or `"key"` are present in `params` with value `value`, | |
`{value, new_params}` is returned where `new_params` is the result | |
of removing `key` from `params`. If `key` is not present in `params`, | |
`{default, params}` is returned. | |
## Examples | |
iex> Params.pop(%{a: 1, b: 2}, :a) | |
{1, %{b: 2}} | |
iex> Params.pop(%{a: 1, b: 2}, "a") | |
{1, %{b: 2}} | |
iex> Params.pop(%{"a" => 1, "b" => 2}, :a) | |
{1, %{"b" => 2}} | |
iex> Params.pop(%{"a" => 1, "b" => 2}, "a") | |
{1, %{"b" => 2}} | |
iex> Params.pop(%{a: 1}, :b) | |
{nil, %{a: 1}} | |
iex> Params.pop(%{a: 1}, "b") | |
{nil, %{a: 1}} | |
iex> Params.pop(%{"a" => 1}, :b) | |
{nil, %{"a" => 1}} | |
iex> Params.pop(%{"a" => 1}, "b") | |
{nil, %{"a" => 1}} | |
iex> Params.pop(%{a: 1}, "b", 3) | |
{3, %{a: 1}} | |
""" | |
@spec pop(t, key, any) :: {any, t} | |
def pop(%{} = params, key, default \\ nil) when is_key(key) do | |
cond do | |
all_atoms?(params) -> Map.pop(params, :"#{key}", default) | |
all_strings?(params) -> Map.pop(params, "#{key}", default) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Lazily returns and removes the value associated with `key` in `params`. | |
If `:key` or `"key"` are present in `params` with value `value`, | |
`{value, new_params}` is returned where `new_params` is the result of | |
removing `key` from `params`. If `key` is not present in `params`, | |
`{fun_result, params}` is returned, where `fun_result` is the result of | |
applying `fun`. | |
This is useful if the default value is very expensive to calculate or | |
generally difficult to setup and teardown again. | |
## Examples | |
iex> Params.pop_lazy(%{a: 1, b: 2}, :a, fn -> | |
...> # some expensive operation here | |
...> 13 | |
...> end) | |
{1, %{b: 2}} | |
iex> Params.pop_lazy(%{a: 1, b: 2}, "a", fn -> 13 end) | |
{1, %{b: 2}} | |
iex> Params.pop_lazy(%{"a" => 1, "b" => 2}, :a, fn -> 13 end) | |
{1, %{"b" => 2}} | |
iex> Params.pop_lazy(%{"a" => 1, "b" => 2}, "a", fn -> 13 end) | |
{1, %{"b" => 2}} | |
iex> Params.pop_lazy(%{a: 1, b: 2}, :c, fn -> 13 end) | |
{13, %{a: 1, b: 2}} | |
iex> Params.pop_lazy(%{a: 1, b: 2}, "c", fn -> 13 end) | |
{13, %{a: 1, b: 2}} | |
iex> Params.pop_lazy(%{"a" => 1, "b" => 2}, :c, fn -> 13 end) | |
{13, %{"a" => 1, "b" => 2}} | |
iex> Params.pop_lazy(%{"a" => 1, "b" => 2}, "c", fn -> 13 end) | |
{13, %{"a" => 1, "b" => 2}} | |
""" | |
@spec pop_lazy(t, key, (() -> any)) :: {any, t} | |
def pop_lazy(%{} = params, key, fun) when is_key(key) and is_function(fun, 0) do | |
cond do | |
all_atoms?(params) -> Map.pop_lazy(params, :"#{key}", fun) | |
all_strings?(params) -> Map.pop_lazy(params, "#{key}", fun) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Puts the given `value` under `key` in `params`. | |
## Examples | |
iex> Params.put(%{a: 1}, :b, 2) | |
%{a: 1, b: 2} | |
iex> Params.put(%{a: 1}, "b", 2) | |
%{a: 1, b: 2} | |
iex> Params.put(%{"a" => 1}, :b, 2) | |
%{"a" => 1, "b" => 2} | |
iex> Params.put(%{"a" => 1}, "b", 2) | |
%{"a" => 1, "b" => 2} | |
iex> Params.put(%{a: 1, b: 2}, :a, 3) | |
%{a: 3, b: 2} | |
iex> Params.put(%{a: 1, b: 2}, "a", 3) | |
%{a: 3, b: 2} | |
iex> Params.put(%{"a" => 1, "b" => 2}, :a, 3) | |
%{"a" => 3, "b" => 2} | |
iex> Params.put(%{"a" => 1, "b" => 2}, "a", 3) | |
%{"a" => 3, "b" => 2} | |
""" | |
@spec put(t, key, any) :: t | |
def put(%{} = params, key, value) when is_key(key) do | |
cond do | |
all_atoms?(params) -> Map.put(params, :"#{key}", value) | |
all_strings?(params) -> Map.put(params, "#{key}", value) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Puts the given `value` under `key` unless the entry `key` already exists | |
in `params`. | |
## Examples | |
iex> Params.put_new(%{a: 1}, :b, 2) | |
%{a: 1, b: 2} | |
iex> Params.put_new(%{a: 1}, "b", 2) | |
%{a: 1, b: 2} | |
iex> Params.put_new(%{"a" => 1}, :b, 2) | |
%{"a" => 1, "b" => 2} | |
iex> Params.put_new(%{"a" => 1}, "b", 2) | |
%{"a" => 1, "b" => 2} | |
iex> Params.put_new(%{a: 1, b: 2}, :a, 3) | |
%{a: 1, b: 2} | |
iex> Params.put_new(%{a: 1, b: 2}, "a", 3) | |
%{a: 1, b: 2} | |
iex> Params.put_new(%{"a" => 1, "b" => 2}, :a, 3) | |
%{"a" => 1, "b" => 2} | |
iex> Params.put_new(%{"a" => 1, "b" => 2}, "a", 3) | |
%{"a" => 1, "b" => 2} | |
""" | |
@spec put_new(t, key, any) :: t | |
def put_new(%{} = params, key, value) when is_key(key) do | |
cond do | |
all_atoms?(params) -> Map.put_new(params, :"#{key}", value) | |
all_strings?(params) -> Map.put_new(params, "#{key}", value) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Evaluates `fun` and puts the result under `key` in `params` unless `key` | |
is already present. | |
This function is useful in case you want to compute the value to put under | |
`key` only if `key` is not already present (e.g., the value is expensive to | |
calculate or generally difficult to setup and teardown again). | |
## Examples | |
iex> Params.put_new_lazy(%{a: 1}, :a, fn -> | |
...> # some expensive operation here | |
...> 3 | |
...> end) | |
%{a: 1} | |
iex> Params.put_new_lazy(%{a: 1}, "a", fn -> 3 end) | |
%{a: 1} | |
iex> Params.put_new_lazy(%{"a" => 1}, :a, fn -> 3 end) | |
%{"a" => 1} | |
iex> Params.put_new_lazy(%{"a" => 1}, "a", fn -> 3 end) | |
%{"a" => 1} | |
iex> Params.put_new_lazy(%{a: 1}, :b, fn -> 3 end) | |
%{a: 1, b: 3} | |
iex> Params.put_new_lazy(%{a: 1}, "b", fn -> 3 end) | |
%{a: 1, b: 3} | |
iex> Params.put_new_lazy(%{"a" => 1}, :b, fn -> 3 end) | |
%{"a" => 1, "b" => 3} | |
iex> Params.put_new_lazy(%{"a" => 1}, "b", fn -> 3 end) | |
%{"a" => 1, "b" => 3} | |
""" | |
@spec put_new_lazy(t, key, (() -> any)) :: t | |
def put_new_lazy(%{} = params, key, fun) when is_key(key) and is_function(fun, 0) do | |
cond do | |
all_atoms?(params) -> Map.put_new_lazy(params, :"#{key}", fun) | |
all_strings?(params) -> Map.put_new_lazy(params, "#{key}", fun) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Returns elements of `params` for which the function `fun` returns `false` | |
or `nil`. | |
See also `filter/2`. | |
## Examples | |
iex> Params.reject(%{a: 1, b: nil}, fn _key, value -> is_nil(value) end) | |
%{a: 1} | |
iex> Params.reject(%{"a": 1, "b": nil}, fn _key, value -> is_nil(value) end) | |
%{"a": 1} | |
""" | |
@spec reject(t, (key, any -> as_boolean(term))) :: t | |
def reject(%{} = params, fun) when is_function(fun, 2) do | |
if all_atoms?(params) or all_strings?(params) do | |
params | |
|> Enum.reject(fn {key, value} -> fun.(key, value) end) | |
|> Enum.into(%{}) | |
else | |
raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Alters the value stored under `key` to `value`, but only if the entry `key` | |
already exists in `params`. | |
If `:key` nor `"key"` are not present in `params`, a `KeyError` exception | |
is raised. | |
## Examples | |
iex> Params.replace!(%{a: 1, b: 2}, :a, 3) | |
%{a: 3, b: 2} | |
iex> Params.replace!(%{a: 1, b: 2}, "a", 3) | |
%{a: 3, b: 2} | |
iex> Params.replace!(%{"a" => 1, "b" => 2}, :a, 3) | |
%{"a" => 3, "b" => 2} | |
iex> Params.replace!(%{"a" => 1, "b" => 2}, "a", 3) | |
%{"a" => 3, "b" => 2} | |
iex> Params.replace!(%{a: 1}, "b", 2) | |
** (KeyError) key :b not found in: %{a: 1} | |
""" | |
@spec replace!(t, key, any) :: t | |
def replace!(%{} = params, key, value) when is_key(key) do | |
cond do | |
all_atoms?(params) -> Map.replace!(params, :"#{key}", value) | |
all_strings?(params) -> Map.replace!(params, "#{key}", value) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Takes all entries corresponding to the given `keys` in `params` and | |
extracts them into a separate `params`. | |
Returns a tuple with the new params and the old params with removed keys. | |
Keys for which there are no entries in `params` are ignored. | |
## Examples | |
iex> Params.split(%{a: 1, b: 2, c: 3}, [:a, "c", :e, "d"]) | |
{%{a: 1, c: 3}, %{b: 2}} | |
iex> Params.split(%{"a" => 1, "b" => 2, "c" => 3}, [:a, "c", :e, "d"]) | |
{%{"a" => 1, "c" => 3}, %{"b" => 2}} | |
""" | |
@spec split(t, [key]) :: {t, t} | |
def split(%{} = params, keys) when is_list(keys), do: do_split(params, keys) | |
def split(%{} = params, %MapSet{} = keys), do: do_split(params, keys) | |
defp do_split(%{} = params, keys) do | |
cond do | |
all_atoms?(params) -> Map.split(params, Enum.map(keys, fn key -> :"#{key}" end)) | |
all_strings?(params) -> Map.split(params, Enum.map(keys, fn key -> "#{key}" end)) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Returns new params with all the key-value pairs in `params` where the key is | |
in `keys`. | |
If `keys` contains keys that are not in `params`, they’re simply ignored. | |
## Examples | |
iex> Params.take(%{a: 1, b: 2, c: 3}, [:a, "c", :e, "d"]) | |
%{a: 1, c: 3} | |
iex> Params.take(%{"a" => 1, "b" => 2, "c" => 3}, [:a, "c", :e, "d"]) | |
%{"a" => 1, "c" => 3} | |
""" | |
@spec take(t, [key]) :: t | |
def take(%{} = params, keys) when is_list(keys), do: do_take(params, keys) | |
def take(%{} = params, %MapSet{} = keys), do: do_take(params, keys) | |
defp do_take(%{} = params, keys) do | |
cond do | |
all_atoms?(params) -> Map.take(params, Enum.map(keys, fn key -> :"#{key}" end)) | |
all_strings?(params) -> Map.take(params, Enum.map(keys, fn key -> "#{key}" end)) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Updates the `key` in `params` with the given function. | |
If `:key` or `"key"` are present in `params` with value `value`, `fun` is | |
invoked with argument `value` and its result is used as the new value of | |
`key`. If `key` is not present in `params`, `initial` is inserted as the | |
value of `key`. The initial value will not be passed through the update | |
function. | |
## Examples | |
iex> Params.update(%{a: 1}, :a, 13, &(&1 * 2)) | |
%{a: 2} | |
iex> Params.update(%{a: 1}, "a", 13, &(&1 * 2)) | |
%{a: 2} | |
iex> Params.update(%{"a" => 1}, :a, 13, &(&1 * 2)) | |
%{"a" => 2} | |
iex> Params.update(%{"a" => 1}, "a", 13, &(&1 * 2)) | |
%{"a" => 2} | |
iex> Params.update(%{a: 1}, :b, 11, &(&1 * 2)) | |
%{a: 1, b: 11} | |
iex> Params.update(%{a: 1}, "b", 11, &(&1 * 2)) | |
%{a: 1, b: 11} | |
iex> Params.update(%{"a" => 1}, :b, 11, &(&1 * 2)) | |
%{"a" => 1, "b" => 11} | |
iex> Params.update(%{"a" => 1}, "b", 11, &(&1 * 2)) | |
%{"a" => 1, "b" => 11} | |
""" | |
@spec update(t, key, any, (any -> any)) :: t | |
def update(%{} = params, key, initial, fun) when is_key(key) and is_function(fun, 1) do | |
cond do | |
all_atoms?(params) -> Map.update(params, :"#{key}", initial, fun) | |
all_strings?(params) -> Map.update(params, "#{key}", initial, fun) | |
true -> raise MixedKeysError, params | |
end | |
end | |
@doc """ | |
Updates `key` with the given function. | |
If `:key` or `"key"` are present in `params` with value `value`, `fun` is | |
invoked with argument `value` and its result is used as the new value of | |
`key`. If `key` is not present in `params`, a `KeyError` exception is raised. | |
## Examples | |
iex> Params.update!(%{a: 1}, :a, &(&1 * 2)) | |
%{a: 2} | |
iex> Params.update!(%{a: 1}, "a", &(&1 * 2)) | |
%{a: 2} | |
iex> Params.update!(%{"a" => 1}, :a, &(&1 * 2)) | |
%{"a" => 2} | |
iex> Params.update!(%{"a" => 1}, "a", &(&1 * 2)) | |
%{"a" => 2} | |
iex> Params.update!(%{a: 1}, "b", &(&1 * 2)) | |
** (KeyError) key :b not found in: %{a: 1} | |
""" | |
@spec update!(t, key, (any -> any)) :: t | |
def update!(%{} = params, key, fun) when is_key(key) and is_function(fun, 1) do | |
cond do | |
all_atoms?(params) -> Map.update!(params, :"#{key}", fun) | |
all_strings?(params) -> Map.update!(params, "#{key}", fun) | |
true -> raise MixedKeysError, params | |
end | |
end | |
defp all_atoms?(params), do: Enum.all?(params, fn {field, _value} -> is_atom(field) end) | |
defp all_strings?(params), do: Enum.all?(params, fn {field, _value} -> is_binary(field) end) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule ParamsTest do | |
use ExUnit.Case, async: true | |
@invalid_key 1 | |
@invalid_keys 1 | |
@invalid_params [] | |
doctest Params | |
test "delete/2 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.delete(%{"a" => 1, b: 2}, :a) | |
end | |
end | |
test "delete/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.delete(@invalid_params, :a) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.delete(%{}, @invalid_key) | |
end | |
end | |
test "drop/2 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.drop(%{"a" => 1, b: 2}, [:a, :b]) | |
end | |
end | |
test "drop/2 works with sets" do | |
assert Params.drop(%{a: 1, b: 2, c: 3}, MapSet.new([:b, "c"])) == %{a: 1} | |
assert Params.drop(%{"a" => 1, "b" => 2, "c" => 3}, MapSet.new([:b, "c"])) == %{"a" => 1} | |
end | |
test "drop/2 works with empty keys" do | |
assert Params.drop(%{a: 1, b: 2, c: 3}, []) == %{a: 1, b: 2, c: 3} | |
assert Params.drop(%{"a" => 1, "b" => 2, "c" => 3}, []) == %{"a" => 1, "b" => 2, "c" => 3} | |
end | |
test "drop/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.drop(@invalid_params, [:a]) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.drop(%{}, @invalid_keys) | |
end | |
end | |
test "equivalent?/2 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.equivalent?(%{"a" => 1, b: 2}, %{}) | |
end | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.equivalent?(%{}, %{"a" => 1, b: 2}) | |
end | |
end | |
test "equivalent?/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.equivalent?(@invalid_params, %{}) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.equivalent?(%{}, @invalid_params) | |
end | |
end | |
test "fetch/2 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.fetch(%{"a" => 1, b: 2}, :a) | |
end | |
end | |
test "fetch/2 does not return :error with nil values" do | |
assert Params.fetch(%{a: nil, b: 2}, :a) == {:ok, nil} | |
end | |
test "fetch/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.fetch(@invalid_params, :a) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.fetch(%{}, @invalid_key) | |
end | |
end | |
test "fetch!/2 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.fetch!(%{"a" => 1, b: 2}, :a) | |
end | |
end | |
test "fetch!/2 does not return :error with nil values" do | |
assert Params.fetch!(%{a: nil, b: 2}, :a) == nil | |
end | |
test "fetch!/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.fetch!(@invalid_params, :a) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.fetch!(%{}, @invalid_key) | |
end | |
end | |
test "filter/2 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.filter(%{"a" => 1, b: 2}, fn _key, value -> not is_nil(value) end) | |
end | |
end | |
test "filter/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.filter(@invalid_params, fn key, value -> value end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
invalid_fun = fn -> {1, 1} end | |
Params.filter(%{}, invalid_fun) | |
end | |
end | |
test "get/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.get(%{"a" => 1, b: 2}, :a) | |
end | |
end | |
test "get/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.get(@invalid_params, :a) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.get(%{}, @invalid_key) | |
end | |
end | |
test "get_and_update/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.get_and_update(%{"a" => 1, b: 2}, :a, fn value -> {value, value} end) | |
end | |
end | |
test "get_and_update/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.get_and_update(@invalid_params, :a, fn value -> {value, value} end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.get_and_update(%{}, @invalid_key, fn value -> {value, value} end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
invalid_fun = fn -> {1, 1} end | |
Params.get_and_update(%{}, :a, invalid_fun) | |
end | |
end | |
test "get_and_update/3 with invalid function" do | |
message = "the given function must return a two-element tuple or :pop, got: 1" | |
assert_raise RuntimeError, message, fn -> | |
Params.get_and_update(%{a: 1}, :a, fn value -> value end) | |
end | |
end | |
test "get_and_update!/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.get_and_update!(%{"a" => 1, b: 2}, :a, fn value -> {value, value} end) | |
end | |
end | |
test "get_and_update!/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.get_and_update!(@invalid_params, :a, fn value -> {value, value} end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.get_and_update!(%{}, @invalid_key, fn value -> {value, value} end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
invalid_fun = fn -> {1, 1} end | |
Params.get_and_update!(%{}, :a, invalid_fun) | |
end | |
end | |
test "get_and_update!/3 raises KeyError when the params does not contain the given key" do | |
assert_raise KeyError, "key :b not found in: %{a: 1}", fn -> | |
Params.get_and_update!(%{a: 1}, :b, &{&1, 2}) | |
end | |
assert_raise KeyError, "key :b not found in: %{a: 1}", fn -> | |
Params.get_and_update!(%{a: 1}, "b", &{&1, 2}) | |
end | |
assert_raise KeyError, "key \"b\" not found in: %{\"a\" => 1}", fn -> | |
Params.get_and_update!(%{"a" => 1}, :b, &{&1, 2}) | |
end | |
assert_raise KeyError, "key \"b\" not found in: %{\"a\" => 1}", fn -> | |
Params.get_and_update!(%{"a" => 1}, "b", &{&1, 2}) | |
end | |
end | |
test "get_and_update!/3 with invalid function" do | |
message = "the given function must return a two-element tuple or :pop, got: 1" | |
assert_raise RuntimeError, message, fn -> | |
Params.get_and_update!(%{a: 1}, :a, fn value -> value end) | |
end | |
end | |
test "get_lazy/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.get_lazy(%{"a" => 1, b: 2}, :a, fn -> 3 end) | |
end | |
end | |
test "get_lazy/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.get_lazy(@invalid_params, :a, fn -> 1 end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.get_lazy(%{}, @invalid_key, fn -> 1 end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
invalid_fun = fn value -> value end | |
Params.get_lazy(%{}, :a, invalid_fun) | |
end | |
end | |
test "has_key?/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.has_key?(%{"a" => 1, b: 2}, :a) | |
end | |
end | |
test "has_key?/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.has_key?(@invalid_params, :a) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.has_key?(%{}, @invalid_key) | |
end | |
end | |
test "has_keys?/2 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.has_keys?(%{"a" => 1, b: 2}, [:a, :b]) | |
end | |
end | |
test "has_keys?/2 works with sets" do | |
assert Params.has_keys?(%{a: 1, b: 2, c: 3}, MapSet.new([:b, "c"])) | |
assert Params.has_keys?(%{"a" => 1, "b" => 2, "c" => 3}, MapSet.new([:b, "c"])) | |
end | |
test "has_keys?/2 works with empty keys" do | |
assert Params.has_keys?(%{a: 1, b: 2, c: 3}, []) | |
assert Params.has_keys?(%{"a" => 1, "b" => 2, "c" => 3}, []) | |
end | |
test "has_keys?/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.has_keys?(@invalid_params, [:a]) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.has_keys?(%{}, @invalid_keys) | |
end | |
end | |
test "merge/2 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.merge(%{"a" => 1, b: 2}, %{}) | |
end | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.merge(%{}, %{"a" => 1, b: 2}) | |
end | |
end | |
test "merge/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.merge(@invalid_params, %{}) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.merge(%{}, @invalid_params) | |
end | |
end | |
test "merge/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.merge(%{"a" => 1, b: 2}, %{}, fn _k, _v1, _v2 -> 1 end) | |
end | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.merge(%{}, %{"a" => 1, b: 2}, fn _k, _v1, _v2 -> 1 end) | |
end | |
end | |
test "merge/3 works with params with different lengths" do | |
# When first map is bigger | |
assert Params.merge(%{a: 1, b: 2, c: 3}, %{"c" => 4, "d" => 5}, fn :c, 3, 4 -> :x end) == | |
%{a: 1, b: 2, c: :x, d: 5} | |
# When second map is bigger | |
assert Params.merge(%{"b" => 2, "c" => 3}, %{a: 1, c: 4, d: 5}, fn "c", 3, 4 -> :x end) == | |
%{"a" => 1, "b" => 2, "c" => :x, "d" => 5} | |
end | |
test "merge/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.merge(@invalid_params, %{}, fn _k, _v1, _v2 -> 1 end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.merge(%{}, @invalid_params, fn _k, _v1, _v2 -> 1 end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.merge(%{}, %{}, fn -> 1 end) | |
end | |
end | |
test "pop/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.pop(%{"a" => 1, b: 2}, :a) | |
end | |
end | |
test "pop/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.pop(@invalid_params, :a) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.pop(%{}, @invalid_key) | |
end | |
end | |
test "pop_lazy/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.pop_lazy(%{"a" => 1, b: 2}, :a, fn -> 1 end) | |
end | |
end | |
test "pop_lazy/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.pop_lazy(@invalid_params, :a, fn -> 1 end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.pop_lazy(%{}, @invalid_key, fn -> 1 end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.pop_lazy(%{}, :a, fn value -> value end) | |
end | |
end | |
test "put/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.put(%{"a" => 1, b: 2}, :a, 1) | |
end | |
end | |
test "put/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.put(@invalid_params, :a, 1) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.put(%{}, @invalid_key, 1) | |
end | |
end | |
test "put_new/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.put_new(%{"a" => 1, b: 2}, :a, 1) | |
end | |
end | |
test "put_new/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.put_new(@invalid_params, :a, 1) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.put_new(%{}, @invalid_key, 1) | |
end | |
end | |
test "put_new_lazy/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.put_new_lazy(%{"a" => 1, b: 2}, :a, fn -> 1 end) | |
end | |
end | |
test "put_new_lazy/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.put_new_lazy(@invalid_params, :a, fn -> 1 end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.put_new_lazy(%{}, @invalid_key, fn -> 1 end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.put_new_lazy(%{}, :a, fn value -> value end) | |
end | |
end | |
test "reject/2 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.reject(%{"a" => 1, b: 2}, fn _key, value -> is_nil(value) end) | |
end | |
end | |
test "reject/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.reject(@invalid_params, fn key, value -> value end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
invalid_fun = fn -> {1, 1} end | |
Params.reject(%{}, invalid_fun) | |
end | |
end | |
test "replace!/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.replace!(%{"a" => 1, b: 2}, :a, 1) | |
end | |
end | |
test "replace!/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.replace!(@invalid_params, :a, 1) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.replace!(%{}, @invalid_key, 1) | |
end | |
end | |
test "split/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.split(%{"a" => 1, b: 2}, [:a]) | |
end | |
end | |
test "split/2 works with sets" do | |
assert Params.split(%{a: 1, b: 2, c: 3}, MapSet.new([:b, "c"])) == {%{b: 2, c: 3}, %{a: 1}} | |
assert Params.split(%{"a" => 1, "b" => 2, "c" => 3}, MapSet.new([:b, "c"])) == | |
{%{"b" => 2, "c" => 3}, %{"a" => 1}} | |
end | |
test "split/2 works with empty keys" do | |
assert Params.split(%{a: 1, b: 2, c: 3}, []) == {%{}, %{a: 1, b: 2, c: 3}} | |
assert Params.split(%{"a" => 1, "b" => 2, "c" => 3}, []) == | |
{%{}, %{"a" => 1, "b" => 2, "c" => 3}} | |
end | |
test "split/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.split(@invalid_params, [:a]) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.split(%{}, @invalid_keys) | |
end | |
end | |
test "take/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.take(%{"a" => 1, b: 2}, [:a]) | |
end | |
end | |
test "take/2 works with sets" do | |
assert Params.take(%{a: 1, b: 2, c: 3}, MapSet.new([:b, "c"])) == %{b: 2, c: 3} | |
assert Params.take(%{"a" => 1, "b" => 2, "c" => 3}, MapSet.new([:b, "c"])) == | |
%{"b" => 2, "c" => 3} | |
end | |
test "take/2 works with empty keys" do | |
assert Params.take(%{a: 1, b: 2, c: 3}, []) == %{} | |
assert Params.take(%{"a" => 1, "b" => 2, "c" => 3}, []) == %{} | |
end | |
test "take/2 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.take(@invalid_params, [:a]) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.take(%{}, @invalid_keys) | |
end | |
end | |
test "update/4 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.update(%{"a" => 1, b: 2}, :a, 1, fn value -> value end) | |
end | |
end | |
test "update/4 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.update(@invalid_params, :a, 1, fn value -> value end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.update(%{}, @invalid_key, 1, fn value -> value end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.update(%{}, :a, 1, fn -> 1 end) | |
end | |
end | |
test "update!/3 raises Params.MixedKeysError with params with mixed keys" do | |
message = | |
"expected params to be a map with atoms or string keys, " <> | |
"got a map with mixed keys: %{:b => 2, \"a\" => 1}" | |
assert_raise Params.MixedKeysError, message, fn -> | |
Params.update!(%{"a" => 1, b: 2}, :a, fn value -> value end) | |
end | |
end | |
test "update!/3 raises FunctionClauseError with invalid args" do | |
assert_raise FunctionClauseError, fn -> | |
Params.update!(@invalid_params, :a, fn value -> value end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.update!(%{}, @invalid_key, fn value -> value end) | |
end | |
assert_raise FunctionClauseError, fn -> | |
Params.update!(%{}, :a, fn -> 1 end) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment