-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
29 changed files
with
1,071 additions
and
63 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,13 +7,20 @@ on: | |
|
||
env: | ||
MIX_ENV: test | ||
KUBECONFIG: test/support/kubeconfig-integration.yaml | ||
|
||
jobs: | ||
code-quality: | ||
name: Code Quality | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 | ||
- uses: engineerd/[email protected] | ||
id: kind | ||
with: | ||
version: v0.20.0 | ||
image: kindest/node:${{ matrix.k8s_version }} | ||
name: kubereq | ||
|
||
- name: Setup elixir | ||
id: beam | ||
|
@@ -47,8 +54,31 @@ jobs: | |
- name: Compile | ||
run: MIX_ENV=test mix compile --warnings-as-errors | ||
|
||
- name: Unit Tests | ||
run: MIX_ENV=test mix test | ||
|
||
- name: Run Credo | ||
run: MIX_ENV=test mix credo --strict | ||
|
||
- name: Run Coverage | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
MIX_ENV: test | ||
run: TEST_WAIT_TIMEOUT=10000 mix coveralls.github --include integration --timeout 20000 | ||
|
||
# Create PLTs if no cache was found | ||
- name: Create PLTs | ||
if: steps.plt-cache.outputs.cache-hit != 'true' | ||
run: | | ||
mkdir -p priv/plts | ||
MIX_ENV=test mix dialyzer --plt | ||
- name: Save PLT cache | ||
id: plt_cache_save | ||
uses: actions/cache/save@v4 | ||
if: steps.plt-cache.outputs.cache-hit != 'true' | ||
with: | ||
key: | | ||
${{ runner.os }}-${{ steps.beam.outputs.elixir-version }}-${{ steps.beam.outputs.ito-version }}-plt-${{ hashFiles('**/mix.lock') }} | ||
path: | | ||
priv/plts | ||
- name: Run dialyzer | ||
run: MIX_ENV=test mix dialyzer --format github |
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
defmodule Kubereq.Access do | ||
@moduledoc """ | ||
Helper module to access maps in lists. | ||
""" | ||
|
||
@doc ~S""" | ||
Returns a function that accesses the first element for which `fun` returns a truthy value. | ||
The returned function is typically passed as an accessor to `Kernel.get_in/2`, | ||
`Kernel.get_and_update_in/3`, and friends. | ||
## Examples | ||
iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}] | ||
iex> get_in(list, [Kubereq.Access.find(&(&1.name == "john")), :salary]) | ||
10 | ||
iex> get_and_update_in(list, [Kubereq.Access.find(&(&1.name == "john")), :salary], fn prev -> | ||
...> {prev, 15} | ||
...> end) | ||
{10, [%{name: "john", salary: 15}, %{name: "francine", salary: 30}]} | ||
`find/1` can also be used to pop elements out of a list or | ||
a key inside of a list: | ||
iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}] | ||
iex> pop_in(list, [Kubereq.Access.find(&(&1.name == "francine"))]) | ||
{%{name: "francine", salary: 30}, [%{name: "john", salary: 10}]} | ||
iex> pop_in(list, [Kubereq.Access.find(&(&1.name == "francine")), :name]) | ||
{"francine", [%{salary: 30}, %{name: "john", salary: 10}]} | ||
When no match is found, the given default is used. This can be used to | ||
specify defaults and safely traverse missing items. | ||
iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}] | ||
iex> get_in(list, [Kubereq.Access.find(&(&1.name == "adam"), %{name: "adam", salary: 50}), :salary]) | ||
50 | ||
iex> get_and_update_in(list, [Kubereq.Access.find(&(&1.name == "adam"), %{name: "adam"}), :salary], fn prev -> | ||
...> {prev, 50} | ||
...> end) | ||
{nil, [%{name: "adam", salary: 50}, %{name: "john", salary: 10}, %{name: "francine", salary: 30}]} | ||
When multiple items exist for which `fun` return a truthy value, the first one is accessed. | ||
iex> list = [%{name: "john", salary: 10}, %{name: "john", salary: 30}] | ||
iex> get_in(list, [Kubereq.Access.find(&(&1.name == "john")), :salary]) | ||
10 | ||
An error is raised if the accessed structure is not a list: | ||
iex> get_in(%{}, [Kubereq.Access.find(&(&1.name == "john"))]) | ||
** (RuntimeError) Kubereq.Access.find/1 expected a list, got: %{} | ||
""" | ||
@spec find((term -> boolean), term()) :: Access.access_fun(data :: list, current_value :: list) | ||
def find(func, default \\ nil) when is_function(func) do | ||
fn op, data, next -> find(op, data, func, default, next) end | ||
end | ||
|
||
defp find(:get, data, func, default, next) when is_list(data) do | ||
data |> Enum.find(default, func) |> next.() | ||
end | ||
|
||
defp find(:get_and_update, data, func, default, next) when is_list(data) do | ||
get_and_update_find(data, func, next, default, false) | ||
end | ||
|
||
defp find(_op, data, _default, _func, _next) do | ||
raise "Kubereq.Access.find/1 expected a list, got: #{inspect(data)}" | ||
end | ||
|
||
@doc ~S""" | ||
Returns a function that accesses the first element for which `fun` returns a truthy value. | ||
The returned function is typically passed as an accessor to `Kernel.get_in/2`, | ||
`Kernel.get_and_update_in/3`, and friends. | ||
Similar to find/2, but the returned function raises if the no item is found for which `fun` returns a truthy value. | ||
## Examples | ||
iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}] | ||
iex> get_in(list, [Kubereq.Access.find!(&(&1.name == "john")), :salary]) | ||
10 | ||
iex> get_and_update_in(list, [Kubereq.Access.find!(&(&1.name == "john")), :salary], fn prev -> | ||
...> {prev, 15} | ||
...> end) | ||
{10, [%{name: "john", salary: 15}, %{name: "francine", salary: 30}]} | ||
`find/1` can also be used to pop elements out of a list or | ||
a key inside of a list: | ||
iex> list = [%{name: "john", salary: 10}, %{name: "francine", salary: 30}] | ||
iex> pop_in(list, [Kubereq.Access.find!(&(&1.name == "francine"))]) | ||
{%{name: "francine", salary: 30}, [%{name: "john", salary: 10}]} | ||
iex> pop_in(list, [Kubereq.Access.find!(&(&1.name == "francine")), :name]) | ||
{"francine", [%{salary: 30}, %{name: "john", salary: 10}]} | ||
iex> get_in(list, [Kubereq.Access.find!(&(&1.name == "adam")), :salary]) | ||
** (ArgumentError) There is no item in the list for which the given function returns a truthy value. | ||
When multiple items exist for which `fun` return a truthy value, the first one is accessed. | ||
iex> list = [%{name: "john", salary: 10}, %{name: "john", salary: 30}] | ||
iex> get_in(list, [Kubereq.Access.find!(&(&1.name == "john")), :salary]) | ||
10 | ||
An error is raised if the accessed structure is not a list: | ||
iex> get_in(%{}, [Kubereq.Access.find!(&(&1.name == "john"))]) | ||
** (RuntimeError) Kubereq.Access.find!/1 expected a list, got: %{} | ||
""" | ||
@spec find!((term -> boolean)) :: Access.access_fun(data :: list, current_value :: list) | ||
def find!(func) when is_function(func) do | ||
fn op, data, next -> find!(op, data, func, next) end | ||
end | ||
|
||
defp find!(:get, data, func, next) when is_list(data) do | ||
case Enum.find(data, func) do | ||
nil -> | ||
raise ArgumentError, | ||
"There is no item in the list for which the given function returns a truthy value." | ||
|
||
item -> | ||
next.(item) | ||
end | ||
end | ||
|
||
defp find!(:get_and_update, data, func, next) when is_list(data) do | ||
get_and_update_find(data, func, next) | ||
end | ||
|
||
defp find!(_op, data, _func, _next) do | ||
raise "Kubereq.Access.find!/1 expected a list, got: #{inspect(data)}" | ||
end | ||
|
||
defp get_and_update_find(data, func, next, default \\ nil, raise? \\ true) do | ||
{value, rest} = | ||
case Enum.find_index(data, func) do | ||
nil when raise? -> | ||
raise ArgumentError, | ||
"There is no item in the list for which the given function returns a truthy value." | ||
|
||
nil -> | ||
{default, data} | ||
|
||
index -> | ||
List.pop_at(data, index) | ||
end | ||
|
||
case next.(value) do | ||
{get, update} -> {get, [update | rest]} | ||
:pop -> {value, rest} | ||
end | ||
end | ||
end |
Oops, something went wrong.