View Source AshPagify.Misc (ash_pagify v1.1.1)
Miscellaneous functions for AshPagify.
Summary
Functions
Convert map string keys to :atom keys. This is useful when you have a map that was created from JSON or other external source and you want to convert the keys to atoms.
Coerce a maybe empty map to nil if it is empty.
Deeply merges two maps or lists, preferring values from the right map or list.
Returns the option with the given key.
Returns the global option derived from a map or a function referenced in the application environment.
Deep merges two lists, preferring values from the right list.
Deeply merges two maps, preferring values from the right map.
Puts a value
under key
only if the value is not nil
, []
, ""
, or %{}
.
Put compiled ash_pagify scopes into the options if they are not already there.
Puts the scopes params of a AshPagify struct into a keyword list only if they don't match the defaults either passed as last argument or loaded on the fly.
Remove nil values from a map or struct. Does not work with nested maps.
Returns the resource option derived from a map or a function reference in the resource ash_pagfiy_options function.
Convert map :atom keys to string keys.
Returns a list of unique keywords from a list of keywords while preserving the order of the first occurrence of each keyword.
Walks a map or list and applies a serializer to the keys.
Functions
Convert map string keys to :atom keys. This is useful when you have a map that was created from JSON or other external source and you want to convert the keys to atoms.
You can specify a list of keys to convert or a depth to which to convert keys. If you specify a depth of 1, only the top level keys will be converted. If you specify a depth of 2, the top level keys and the keys of any maps in the top level will be converted. And so on.
If you set the existing? option to true, the function will use
the String.to_existing_atom/1
function to convert the keys.
List of options:
keys
: A list of keys to convert. If a key is not in the list, it will not be converted. Default is an empty list and all keys will be converted.depth
: The depth to which to convert keys. Default is nil and all keys will be converted.existing?
: If true, the function will useString.to_existing_atom/1
to convert the keys. Default is false.
Example
iex> AshPagify.Misc.atomize_keys(%{"a" => 1, "b" => 2})
%{a: 1, b: 2}
iex> AshPagify.Misc.atomize_keys(%{"a" => 1, "b" => %{"c" => 3}})
%{a: 1, b: %{c: 3}}
iex> AshPagify.Misc.atomize_keys(%{"a" => 1, "b" => %{"c" => 3}}, keys: ["b"])
%{"a" => 1, b: %{"c" => 3}}
iex> AshPagify.Misc.atomize_keys(%{"a" => 1, "b" => %{"c" => 3}}, keys: ["b", "c"])
%{"a" => 1, b: %{c: 3}}
iex> AshPagify.Misc.atomize_keys(%{"a" => 1, "b" => %{"c" => 3}}, keys: ["b", "d"], depth: 1)
%{"a" => 1, b: %{"c" => 3}}
iex> AshPagify.Misc.atomize_keys(%{"a" => 1, "b" => %{"c" => 3}}, keys: ["b", "c"], depth: 2)
%{"a" => 1, b: %{c: 3}}
Coerce a maybe empty map to nil if it is empty.
Example
iex> AshPagify.Misc.coerce_maybe_empty_map(%{})
nil
iex> AshPagify.Misc.coerce_maybe_empty_map(%{a: 1})
%{a: 1}
iex> AshPagify.Misc.coerce_maybe_empty_map(nil)
nil
Deeply merges two maps or lists, preferring values from the right map or list.
If a key exists in both maps or lists, and both values are maps or lists as well, these can be merged recursively. If a key exists in both maps or lists, but at least one of the values is NOT a map or list, we fall back to standard merge behavior, preferring the value on the right.
Example:
iex> AshPagify.Misc.deep_merge(%{a: 1, b: %{c: 2}}, %{b: %{d: 3}})
%{a: 1, b: %{c: 2, d: 3}}
one level of maps without conflict
iex> AshPagify.Misc.deep_merge(%{a: 1}, %{b: 2})
%{a: 1, b: 2}
two levels of maps without conflict
iex> AshPagify.Misc.deep_merge(%{a: [%{b: 1}]}, %{a: [%{c: 3}]})
%{a: [%{b: 1}, %{c: 3}]}
three levels of maps without conflict
iex> AshPagify.Misc.deep_merge(%{a: %{b: %{c: 1}}}, %{a: %{b: %{d: 2}}})
%{a: %{b: %{c: 1, d: 2}}}
non-map value in left
iex> AshPagify.Misc.deep_merge(%{a: 1}, %{a: %{b: 2}})
%{a: %{b: 2}}
non-map value in right
iex> AshPagify.Misc.deep_merge(%{a: %{b: 1}}, %{a: 2})
%{a: 2}
non-map value in both
iex> AshPagify.Misc.deep_merge(%{a: 1}, %{a: 2})
%{a: 2}
map of list
iex> AshPagify.Misc.deep_merge(%{a: [1, 2]}, %{a: [2, 3]})
%{a: [1, 2, 3]}
map of list of map
iex> AshPagify.Misc.deep_merge(%{a: [%{b: 1}, %{c: 2}]}, %{a: [%{c: 3}, %{d: 4}]})
%{a: [%{b: 1}, %{c: 3}, %{d: 4}]}
map of different types
iex> AshPagify.Misc.deep_merge(%{a: [1, 2]}, %{a: %{b: 2}})
%{a: %{b: 2}}
map of list of different types
iex> AshPagify.Misc.deep_merge(%{a: [1, 2]}, %{a: [%{b: 2}]})
%{a: [1, 2, %{b: 2}]}
Returns the option with the given key.
The look-up order is:
- the keyword list passed as the second argument
- the Ash.Resource resource, if the passed list includes the
:for
option - the application environment
- the AshPagify default value if defined
- the default passed as the last argument
For the :scopes
option, the function will deep merge the options
in reverse order (keyword overrides resource, resource overrides global, etc.)
Examples for :scopes
iex> alias AshPagify.Factory.Post
iex> opts = [
...> scopes: %{
...> role: [
...> %{name: :user, filter: %{name: "changed"}},
...> %{name: :other, filter: %{name: "other"}}
...> ],
...> status: [
...> %{name: :all, filter: nil, default?: true},
...> %{name: :active, filter: %{age: %{lt: 10}}},
...> %{name: :inactive, filter: %{age: %{gte: 10}}}
...> ]
...> },
...> for: Post
...> ]
iex> get_option(:scopes, opts, %{
...> role: [
...> %{name: :default, filter: %{author: "Default"}}
...> ]
...> })
%{
role: [
%{name: :admin, filter: %{author: "John"}},
%{name: :user, filter: %{name: "changed"}},
%{name: :other, filter: %{name: "other"}},
%{name: :default, filter: %{author: "Default"}}
],
status: [
%{name: :inactive, filter: %{age: %{gte: 10}}},
%{name: :all, filter: nil, default?: true},
%{name: :active, filter: %{age: %{lt: 10}}}
]
}
Returns the global option derived from a map or a function referenced in the application environment.
Deep merges two lists, preferring values from the right list.
If a key exists in both lists, and both values are lists as well, these can be merged recursively. If a key exists in both lists, but at least one of the values is NOT a list, we fall back to standard merge behavior, preferring the value on the right.
Example:
iex> list_merge(
...> [aria: [role: "navigation"]],
...> [aria: [label: "pagination"]]
...> )
[aria: [role: "navigation", label: "pagination"]]
iex> list_merge(
...> [class: "a"],
...> [class: "b"]
...> )
[class: "b"]
Deeply merges two maps, preferring values from the right map.
If a key exists in both maps, and both values are maps as well, these can be merged recursively. If a key exists in both maps, but at least one of the values is NOT a map, we fall back to standard merge behavior, preferring the value on the right.
Example:
iex> AshPagify.Misc.map_merge(%{a: 1, b: %{c: 2}}, %{b: %{d: 3}})
%{a: 1, b: %{c: 2, d: 3}}
one level of maps without conflict
iex> AshPagify.Misc.map_merge(%{a: 1}, %{b: 2})
%{a: 1, b: 2}
two levels of maps without conflict
iex> AshPagify.Misc.map_merge(%{a: %{b: 1}}, %{a: %{c: 3}})
%{a: %{b: 1, c: 3}}
three levels of maps without conflict
iex> AshPagify.Misc.map_merge(%{a: %{b: %{c: 1}}}, %{a: %{b: %{d: 2}}})
%{a: %{b: %{c: 1, d: 2}}}
non-map value in left
iex> AshPagify.Misc.map_merge(%{a: 1}, %{a: %{b: 2}})
%{a: %{b: 2}}
non-map value in right
iex> AshPagify.Misc.map_merge(%{a: %{b: 1}}, %{a: 2})
%{a: 2}
non-map value in both
iex> AshPagify.Misc.map_merge(%{a: 1}, %{a: 2})
%{a: 2}
Puts a value
under key
only if the value is not nil
, []
, ""
, or %{}
.
If a :default
value is passed, it only puts the value into the list if the
value does not match the default value.
iex> maybe_put([], :a, "b")
[a: "b"]
iex> maybe_put([], :a, nil)
[]
iex> maybe_put([], :a, [])
[]
iex> maybe_put([], :a, %{})
[]
iex> maybe_put([], :a, "")
[]
iex> maybe_put([], :a, "a", "a")
[]
iex> maybe_put([], :a, "a", "b")
[a: "a"]
@spec maybe_put_compiled_scopes(Ash.Query.t() | Ash.Resource.t(), Keyword.t()) :: Keyword.t()
Put compiled ash_pagify scopes into the options if they are not already there.
Example
iex> alias AshPagify.Factory.Post
iex> AshPagify.Misc.maybe_put_compiled_scopes(Post)
[
__compiled_default_scopes: %{status: :all},
__compiled_scopes: %{
role: [
%{name: :admin, filter: %{author: "John"}},
%{name: :user, filter: %{author: "Doe"}}
],
status: [
%{name: :all, filter: nil, default?: true},
%{name: :active, filter: %{age: %{lt: 10}}},
%{name: :inactive, filter: %{age: %{gte: 10}}}
]
}
]
Or with default scopes passed as opts
iex> alias AshPagify.Factory.Post
iex> scopes = %{role: [%{name: :user, filter: %{author: "Doe"}, default?: true}]}
iex> AshPagify.Misc.maybe_put_compiled_scopes(Post, [scopes: scopes])
[
__compiled_default_scopes: %{role: :user, status: :all},
__compiled_scopes: %{
role: [
%{name: :admin, filter: %{author: "John"}},
%{name: :user, filter: %{author: "Doe"}, default?: true}
],
status: [
%{name: :all, filter: nil, default?: true},
%{name: :active, filter: %{age: %{lt: 10}}},
%{name: :inactive, filter: %{age: %{gte: 10}}}
]
},
scopes: scopes
]
@spec maybe_put_scopes(Keyword.t(), AshPagify.t(), Keyword.t()) :: Keyword.t()
Puts the scopes params of a AshPagify struct into a keyword list only if they don't match the defaults either passed as last argument or loaded on the fly.
Example:
iex> maybe_put_scopes([], %AshPagify{scopes: %{status: :inactive}}, default_scopes: %{status: :active})
[scopes: %{status: :inactive}]
iex> maybe_put_scopes([], %AshPagify{scopes: %{status: :active}}, default_scopes: %{status: :active})
[]
iex> alias AshPagify.Factory.Post
iex> maybe_put_scopes([], %AshPagify{scopes: %{status: :active}}, for: Post)
[scopes: %{status: :active}]
Remove nil values from a map or struct. Does not work with nested maps.
Example
iex> AshPagify.Misc.remove_nil_values(%{a: 1, b: nil, c: 3})
%{a: 1, c: 3}
iex> AshPagify.Misc.remove_nil_values(%{a: 1, b: %{c: nil, d: 4}})
%{a: 1, b: %{c: nil, d: 4}}
Returns the resource option derived from a map or a function reference in the resource ash_pagfiy_options function.
Convert map :atom keys to string keys.
You can specify a list of keys to convert or a depth to which to convert keys. If you specify a depth of 1, only the top level keys will be converted. If you specify a depth of 2, the top level keys and the keys of any maps in the top level will be converted. And so on.
List of options:
keys
: A list of keys to convert. If a key is not in the list, it will not be converted. Default is an empty list and all keys will be converted.depth
: The depth to which to convert keys. Default is nil and all keys will be converted.
Example
iex> AshPagify.Misc.stringify_keys(%{a: 1, b: 2})
%{"a" => 1, "b" => 2}
iex> AshPagify.Misc.stringify_keys(%{a: 1, b: %{c: 3}})
%{"a" => 1, "b" => %{"c" => 3}}
iex> AshPagify.Misc.stringify_keys(%{a: 1, b: %{c: 3}}, keys: [:b])
%{:a => 1, "b" => %{c: 3}}
iex> AshPagify.Misc.stringify_keys(%{a: 1, b: %{c: 3}}, keys: [:b, :c])
%{:a => 1, "b" => %{"c" => 3}}
iex> AshPagify.Misc.stringify_keys(%{a: 1, b: %{c: 3}}, keys: [:b, :d], depth: 1)
%{:a => 1, "b" => %{c: 3}}
iex> AshPagify.Misc.stringify_keys(%{a: 1, b: %{c: 3}}, keys: [:b, :c], depth: 2)
%{:a => 1, "b" => %{"c" => 3}}
Returns a list of unique keywords from a list of keywords while preserving the order of the first occurrence of each keyword.
Example
iex> AshPagify.Misc.unique_keywords([:a, :b, :a, :c, :b])
[:a, :b, :c]
iex> AshPagify.Misc.unique_keywords([a: 1, b: 2, a: 3, c: 4, b: 5])
[a: 1, b: 2, c: 4]
walk(map_or_list, serializer \\ &default_serializer/2, opts \\ [], current_depth \\ 1)
View SourceWalks a map or list and applies a serializer to the keys.
The serializer function receives the key and the opts. The serializer function should return the new key.
The walk function will walk the map or list and apply the serializer to the keys. If the depth is specified and it is reached, the serializer will not be applied to the keys at that depth.
The serializer function can be used to convert the keys to atoms, strings, or any other format.
Example
iex> walk(%{"a" => 1, "b" => %{"c" => 3}}, fn key, _opts -> String.to_atom(key) end)
%{a: 1, b: %{c: 3}}
iex> walk(%{"a" => 1, "b" => %{"c" => 3}}, fn key, _opts -> String.to_atom(key) end, depth: 1)
%{b: %{"c" => 3}, a: 1}