Packages

ash

1.36.13
3.29.3 3.29.2 3.29.1 3.29.0 3.28.0 3.27.8 3.27.7 3.27.6 3.27.5 3.27.4 3.27.3 3.27.2 3.27.1 3.27.0 3.26.0 3.25.2 3.25.1 3.25.0 retired 3.24.7 3.24.6 3.24.5 3.24.4 3.24.3 3.24.2 3.24.1 3.24.0 3.23.1 3.23.0 3.22.2 3.22.1 3.22.0 3.21.3 3.21.2 3.21.1 3.21.0 3.20.0 3.19.3 3.19.2 3.19.1 3.19.0 3.18.0 3.17.1 3.17.0 3.16.0 3.15.0 3.14.1 3.14.0 retired 3.13.2 3.13.1 3.13.0 3.12.0 3.11.3 3.11.2 3.11.1 3.11.0 3.10.1 3.10.0 3.9.0 3.8.0 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0 retired 3.6.3 retired 3.6.2 3.6.1 3.6.0 3.5.43 3.5.42 3.5.41 3.5.40 3.5.39 3.5.38 3.5.37 3.5.36 3.5.35 3.5.34 3.5.33 3.5.32 3.5.31 3.5.30 3.5.29 3.5.28 3.5.27 3.5.26 3.5.25 3.5.24 3.5.23 3.5.22 3.5.21 3.5.20 3.5.19 3.5.18 3.5.17 3.5.16 3.5.15 3.5.14 3.5.13 3.5.12 3.5.11 3.5.10 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.74 retired 3.4.73 3.4.72 3.4.71 3.4.70 3.4.69 3.4.68 3.4.67 3.4.66 3.4.65 3.4.64 3.4.63 3.4.62 3.4.61 3.4.60 3.4.59 3.4.58 3.4.57 3.4.56 3.4.55 3.4.54 3.4.53 3.4.52 3.4.51 3.4.50 3.4.49 3.4.48 3.4.47 3.4.46 3.4.45 3.4.44 3.4.43 3.4.42 3.4.41 3.4.40 3.4.39 3.4.38 3.4.37 3.4.36 3.4.35 3.4.34 3.4.33 3.4.32 3.4.31 3.4.30 3.4.29 3.4.28 3.4.27 3.4.26 3.4.25 3.4.24 3.4.23 3.4.22 3.4.21 3.4.20 3.4.19 3.4.18 3.4.17 3.4.16 3.4.15 3.4.14 3.4.13 3.4.12 3.4.11 3.4.10 3.4.9 3.4.8 3.4.7 3.4.6 3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.3 3.3.2 3.3.1 3.3.0 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 3.2.0 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.16 3.0.15 3.0.14 3.0.13 3.0.12 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 3.0.0-rc.46 3.0.0-rc.45 3.0.0-rc.44 3.0.0-rc.43 3.0.0-rc.42 3.0.0-rc.41 3.0.0-rc.40 3.0.0-rc.39 3.0.0-rc.38 3.0.0-rc.37 3.0.0-rc.36 3.0.0-rc.35 3.0.0-rc.34 3.0.0-rc.33 3.0.0-rc.32 3.0.0-rc.31 3.0.0-rc.29 3.0.0-rc.28 3.0.0-rc.27 3.0.0-rc.26 3.0.0-rc.25 3.0.0-rc.24 3.0.0-rc.23 3.0.0-rc.22 3.0.0-rc.21 3.0.0-rc.20 3.0.0-rc.19 3.0.0-rc.18 3.0.0-rc.17 3.0.0-rc.16 3.0.0-rc.15 3.0.0-rc.14 3.0.0-rc.13 3.0.0-rc.12 3.0.0-rc.11 3.0.0-rc.10 3.0.0-rc.9 3.0.0-rc.8 3.0.0-rc.7 3.0.0-rc.6 3.0.0-rc.5 3.0.0-rc.4 3.0.0-rc.3 3.0.0-rc.1 3.0.0-rc.0 2.21.15 2.21.14 2.21.13 2.21.12 2.21.11 2.21.10 2.21.9 2.21.8 2.21.7 2.21.6 2.21.5 2.21.4 2.21.3 2.21.2 2.21.1 2.21.0 2.20.3 2.20.2 2.20.1 2.20.0 2.19.14 2.19.13 2.19.12 2.19.11 2.19.10 2.19.9 2.19.8 2.19.7 2.19.6 2.19.5 2.19.4 2.19.3 retired 2.19.2 retired 2.19.1 retired 2.19.0 retired 2.18.2 2.18.1 2.18.0 2.17.24 2.17.23 2.17.22 2.17.21 2.17.20 2.17.19 2.17.18 2.17.17 2.17.16 2.17.15 2.17.14 2.17.13 2.17.12 2.17.11 2.17.10 2.17.9 2.17.8 2.17.7 2.17.6 2.17.5 2.17.4 2.17.3 2.17.2 2.17.1 2.17.0 2.16.1 2.16.0 2.15.20 2.15.19 2.15.18 2.15.17 2.15.16 2.15.15 2.15.14 2.15.13 2.15.12 2.15.11 2.15.10 2.15.9 2.15.8 2.15.7 2.15.6 2.15.5 2.15.4 2.15.2 2.15.1 2.15.0 2.14.21 2.14.20 2.14.19 2.14.18 2.14.17 2.14.16 2.14.15 2.14.14 2.14.13 2.14.12 2.14.11 2.14.10 2.14.9 2.14.8 2.14.7 2.14.6 2.14.5 2.14.4 2.14.3 2.14.2 2.14.1 2.14.0 2.13.4 retired 2.13.3 2.13.2 2.13.1 2.13.0 2.12.1 2.12.0 2.11.11 2.11.10 2.11.9 2.11.8 2.11.7 2.11.6 2.11.5 2.11.4 2.11.3 2.11.2 2.11.1 2.11.0 2.11.0-rc.3 2.11.0-rc.2 2.11.0-rc.1 2.11.0-rc.0 2.10.2 2.10.1 2.10.0 2.9.29 2.9.28 2.9.27 2.9.26 2.9.25 2.9.24 2.9.23 2.9.22 2.9.21 2.9.20 2.9.19 2.9.18 2.9.17 2.9.16 2.9.15 2.9.14 2.9.13 2.9.12 2.9.11 2.9.10 2.9.9 2.9.8 2.9.7 2.9.6 2.9.5 2.9.4 2.9.3 2.9.2 2.9.1 2.9.0 2.8.1 2.8.0 2.7.1 2.7.0 2.6.31 2.6.30 2.6.29 2.6.28 2.6.27 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.16 2.5.15 2.5.14 2.5.13 2.5.12 2.5.11 2.5.10 2.5.9 2.5.8 2.5.7 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.5.0-rc.6 2.5.0-rc.5 2.5.0-rc.4 2.5.0-rc.3 2.5.0-rc.2 2.5.0-rc.1 2.5.0-rc.0 2.4.30 2.4.29 2.4.28 2.4.27 2.4.26 2.4.25 2.4.24 2.4.23 2.4.22 2.4.21 2.4.20 2.4.19 2.4.18 2.4.17 2.4.16 2.4.15 2.4.14 2.4.13 2.4.12 2.4.11 2.4.10 2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 2.0.0-rc.15 2.0.0-rc.14 2.0.0-rc.13 2.0.0-rc.12 2.0.0-rc.11 2.0.0-rc.10 2.0.0-rc.9 2.0.0-rc.8 2.0.0-rc.7 2.0.0-rc.6 2.0.0-rc.5 2.0.0-rc.4 2.0.0-rc.3 2.0.0-rc.2 2.0.0-rc.1 2.0.0-rc.0 2.0.0-pre.8 2.0.0-pre.7 2.0.0-pre.6 2.0.0-pre.5 2.0.0-pre.4 2.0.0-pre.3 2.0.0-pre.2 2.0.0-pre.1 2.0.0-pre.0 1.53.3 1.53.2 1.53.0 1.52.0-rc.22 1.52.0-rc.21 1.52.0-rc.20 1.52.0-rc.19 1.52.0-rc.18 1.52.0-rc.17 1.52.0-rc.16 1.52.0-rc.15 1.52.0-rc.14 1.52.0-rc.13 1.52.0-rc.12 1.52.0-rc.11 1.52.0-rc.10 1.52.0-rc.9 1.52.0-rc.8 1.52.0-rc.7 1.52.0-rc.6 1.52.0-rc.5 1.52.0-rc.4 1.52.0-rc.3 1.52.0-rc.2 1.52.0-rc.1 1.52.0-rc.0 1.51.2 1.51.1 retired 1.51.0 1.50.21 1.50.20 1.50.19 1.50.18 1.50.17 1.50.16 1.50.15 1.50.14 1.50.13 1.50.12 1.50.11 1.50.10 1.50.9 1.50.8 1.50.7 1.50.6 1.50.5 1.50.4 1.50.3 1.50.2 1.50.1 1.50.0 1.49.0 1.48.0-rc.30 1.48.0-rc.29 1.48.0-rc.28 1.48.0-rc.27 1.48.0-rc.26 1.48.0-rc.25 1.48.0-rc.24 1.48.0-rc.23 1.48.0-rc.22 1.48.0-rc.21 1.48.0-rc.20 1.48.0-rc.19 1.48.0-rc.18 1.48.0-rc.17 1.48.0-rc.16 1.48.0-rc.15 1.48.0-rc.14 1.48.0-rc.13 1.48.0-rc.12 1.48.0-rc.11 1.48.0-rc.10 1.48.0-rc.9 1.48.0-rc.8 1.48.0-rc.7 1.48.0-rc.6 1.48.0-rc.5 1.48.0-rc.4 1.48.0-rc.3 1.48.0-rc.2 1.48.0-rc.1 1.48.0-rc.0 1.47.12 1.47.11 1.47.10 1.47.9 1.47.8 1.47.7 1.47.6 1.47.5 1.47.4 1.47.3 1.47.2 1.47.1 1.47.0 1.46.13 1.46.12 1.46.11 1.46.10 1.46.9 1.46.8 1.46.7 1.46.6 1.46.5 1.46.4 1.46.3 1.46.2 1.46.1 1.46.0 1.45.0-rc9 1.45.0-rc8 1.45.0-rc7 1.45.0-rc6 1.45.0-rc5 1.45.0-rc4 1.45.0-rc3 1.45.0-rc20 1.45.0-rc2 1.45.0-rc19 1.45.0-rc18 1.45.0-rc17 1.45.0-rc16 1.45.0-rc15 1.45.0-rc14 1.45.0-rc13 1.45.0-rc12 1.45.0-rc11 1.45.0-rc10 1.45.0-rc1 1.45.0-rc0 1.44.13 1.44.12 1.44.11 1.44.10 1.44.9 1.44.8 1.44.7 1.44.6 1.44.5 1.44.4 1.44.3 1.44.2 1.44.1 1.44.0 1.43.12 1.43.11 1.43.10 1.43.9 1.43.8 1.43.7 1.43.6 1.43.5 1.43.4 1.43.3 1.43.2 1.43.1 1.43.0 1.42.0 1.41.12 1.41.11 1.41.10 1.41.9 1.41.8 1.41.7 1.41.6 1.41.5 1.41.4 1.41.3 1.41.2 1.41.1 1.41.0 1.40.0 1.39.7 1.39.6 1.39.5 1.39.4 1.39.3 1.39.2 1.39.1 1.39.0 1.38.0 1.37.2 1.37.1 1.37.0 1.36.22 1.36.21 1.36.19 1.36.18 1.36.17 1.36.16 1.36.15 1.36.14 1.36.13 1.36.12 1.36.11 1.36.10 1.36.9 1.36.8 1.36.7 1.36.6 1.36.5 1.36.4 1.36.3 1.36.2 1.36.0 1.35.1 1.35.0 1.34.9 1.34.8 1.34.7 1.34.6 1.34.5 1.34.4 1.34.3 1.34.2 1.34.1 1.34.0 1.33.0 1.32.2 1.32.1 1.32.0 1.31.1 1.31.0 1.30.2 1.30.1 1.29.0-rc1 1.29.0-rc0 1.28.1 1.28.0 1.27.1 1.27.0 1.26.13 1.26.12 1.26.11 1.26.10 1.26.9 1.26.8 1.26.7 1.26.6 1.26.5 1.26.4 1.26.2 1.26.1 1.26.0 1.25.8 1.25.7 1.25.6 1.25.5 1.25.4 1.25.3 1.25.2 1.25.1 1.25.0 1.24.2 1.24.1 1.24.0 1.23.3 1.23.2 1.23.1 1.23.0 1.22.1 1.22.0 1.20.1 1.20.0 1.19.1 1.19.0 1.18.1 1.18.0 1.17.1 1.17.0 1.16.2 1.15.1 1.15.0 1.14.0 1.13.4 1.13.3 1.13.2 1.13.1 1.13.0 1.12.0 1.11.1 1.11.0 1.10.0 1.9.0 1.8.0 1.7.0 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.1 1.5.0 1.4.1 1.4.0 1.3.1 1.3.0 1.2.1 1.2.0 1.1.3 1.1.2 1.1.0 1.0.3 1.0.2 1.0.1 1.0.0 0.13.1 0.13.0 0.12.0 0.10.0 0.9.1 0.9.0 0.8.0 0.7.0 0.6.5 0.6.4 0.6.3 0.6.2 0.6.1 0.6.0 0.5.2 0.5.1 0.5.0 0.4.0 0.3.0 0.2.0 0.1.9 0.1.8 0.1.3 0.1.1 0.1.0

A declarative, extensible framework for building Elixir applications.

Security advisory: This version has known vulnerabilities. View advisories

Current section

Files

Jump to
ash lib ash query boolean_expression.ex
Raw

lib/ash/query/boolean_expression.ex

defmodule Ash.Query.BooleanExpression do
@moduledoc "Represents a boolean expression"
@dialyzer {:nowarn_function, optimized_new: 4}
alias Ash.Query.Operator.{Eq, In, NotEq}
alias Ash.Query.Ref
defstruct [:op, :left, :right]
def new(_, nil, nil), do: nil
def new(_, left, nil), do: left
def new(_, nil, right), do: right
def new(op, left, right) do
%__MODULE__{op: op, left: left, right: right}
end
# In many cases we could actually just return `true/false` directly because we know
# statements are contradictions. However, that would likely confuse users. For example:
# `Ash.Query.filter(Resource, x == 1 and x in [2, 3])` becomes `#Ash.Query<filter: false>`
# We may want to go down this route some day, but for now we simply use this to combine
# statements where possible, which helps with authorization logic that leverages the query
# For example, `x in [1, 2] or x == 3` becomes `x in [1, 2, 3]`, and `x in [1, 2, 3] and x != 1`
# becomes `x in [2, 3]`
def optimized_new(op, left, right, current_op \\ :and)
def optimized_new(_, nil, nil, _), do: nil
def optimized_new(:and, false, _, _), do: false
def optimized_new(:and, _, false, _), do: false
def optimized_new(:or, true, _, _), do: true
def optimized_new(:or, _, true, _), do: true
def optimized_new(_, nil, right, _), do: right
def optimized_new(_, left, nil, _), do: left
def optimized_new(
op,
%__MODULE__{op: op} = left_expr,
%__MODULE__{
op: op,
left: left,
right: right
},
op
) do
optimized_new(op, optimized_new(op, left_expr, left, op), right, op)
end
def optimized_new(op, %__MODULE__{} = left, %__MODULE__{} = right, _) do
do_new(op, left, right)
end
def optimized_new(op, left, %__MODULE__{} = right, current_op) do
optimized_new(op, right, left, current_op)
end
def optimized_new(op, %In{} = left, %NotEq{} = right, current_op) do
optimized_new(op, right, left, current_op)
end
def optimized_new(op, %In{} = left, %Eq{} = right, current_op) do
optimized_new(op, right, left, current_op)
end
def optimized_new(op, %Eq{right: %Ref{}} = left, right, _) do
do_new(op, left, right)
end
def optimized_new(op, left, %Eq{right: %Ref{}} = right, _) do
do_new(op, left, right)
end
def optimized_new(op, %NotEq{right: %Ref{}} = left, right, _) do
do_new(op, left, right)
end
def optimized_new(op, left, %NotEq{right: %Ref{}} = right, _) do
do_new(op, left, right)
end
def optimized_new(
:or,
%Eq{left: left, right: value} = left_op,
%In{left: left, right: %{__struct__: MapSet} = mapset} = right,
_
) do
if can_optimize?(value) do
%{right | right: MapSet.put(mapset, value)}
else
do_new(:or, left_op, right)
end
end
def optimized_new(
:or,
%NotEq{left: left, right: value} = left_op,
%In{left: left, right: %{__struct__: MapSet} = mapset} = right,
_
) do
if can_optimize?(value) do
without = MapSet.delete(mapset, value)
case MapSet.size(without) do
0 ->
do_new(:or, left_op, right)
1 ->
%Eq{left: left, right: Enum.at(without, 0)}
_ ->
%{right | right: without}
end
else
do_new(:or, left_op, right)
end
end
def optimized_new(
:and,
%Eq{left: left, right: value} = left_expr,
%In{left: left, right: %{__struct__: MapSet} = mapset} = right,
_
) do
if can_optimize?(value) do
if MapSet.member?(mapset, value) do
left_expr
else
do_new(:and, left_expr, right)
end
else
do_new(:and, left_expr, right)
end
end
def optimized_new(
:and,
%NotEq{left: left, right: value} = left_op,
%In{left: left, right: %{__struct__: MapSet} = mapset} = right_expr,
_
) do
if can_optimize?(value) do
%{right_expr | right: MapSet.delete(mapset, value)}
else
do_new(:and, left_op, right_expr)
end
end
def optimized_new(
op,
%NotEq{} = left,
%Eq{} = right,
current_op
) do
optimized_new(op, right, left, current_op)
end
def optimized_new(
:or,
%Eq{left: left, right: left_value} = left_expr,
%Eq{left: left, right: right_value} = right_expr,
_
) do
if can_optimize?(left_value) && can_optimize?(right_value) do
%In{left: left, right: MapSet.new([left_value, right_value])}
else
do_new(:or, left_expr, right_expr)
end
end
def optimized_new(
:and,
%Eq{left: left, right: left_value} = left_expr,
%Eq{left: left, right: right_value} = right_expr,
_
) do
if can_optimize?(left_value) && can_optimize?(right_value) && left_value == right_value do
left_expr
else
do_new(:and, left_expr, right_expr)
end
end
def optimized_new(
:and,
%NotEq{left: left, right: left_value} = left_expr,
%NotEq{left: left, right: right_value} = right_expr,
_
) do
if can_optimize?(left_value) && can_optimize?(right_value) && left_value == right_value do
left_value
else
do_new(:and, left_expr, right_expr)
end
end
def optimized_new(
:or,
%In{left: left, right: %{__struct__: MapSet} = left_values},
%In{left: left, right: %{__struct__: MapSet} = right_values} = right_expr,
_
) do
%{right_expr | right: MapSet.union(left_values, right_values)}
end
def optimized_new(
:and,
%In{left: left, right: left_values} = left_expr,
%In{left: left, right: right_values} = right_expr,
_
) do
if can_optimize?(left_values) && can_optimize?(right_values) do
intersection = MapSet.intersection(left_values, right_values)
case MapSet.size(intersection) do
0 -> do_new(:and, left_expr, right_expr)
1 -> %Eq{left: left, right: Enum.at(intersection, 0)}
_ -> %{right_expr | right: intersection}
end
else
do_new(:and, left_expr, right_expr)
end
end
def optimized_new(
op,
%__MODULE__{left: left, right: right} = left_expr,
right_expr,
op
) do
case right_expr do
%In{} = in_op ->
with {:left, nil} <- {:left, Ash.Filter.find(left, &simplify?(&1, in_op))},
{:right, nil} <- {:right, Ash.Filter.find(right, &simplify?(&1, in_op))} do
do_new(op, left_expr, in_op)
else
{:left, _} ->
%{left_expr | left: optimized_new(op, left, in_op)}
{:right, _} ->
%{left_expr | right: optimized_new(op, right, in_op)}
end
%Eq{} = eq_op ->
with {:left, nil} <- {:left, Ash.Filter.find(left, &simplify?(&1, eq_op))},
{:right, nil} <- {:right, Ash.Filter.find(right, &simplify?(&1, eq_op))} do
do_new(op, left_expr, eq_op)
else
{:left, _} ->
%{left_expr | left: optimized_new(op, left, eq_op)}
{:right, _} ->
%{left_expr | right: optimized_new(op, right, eq_op)}
end
_ ->
do_new(op, left_expr, right_expr)
end
end
def optimized_new(op, left, right, _) do
# TODO: more optimization passes!
# Remove predicates that are on both sides of an `and`
# if a predicate is on both sides of an `or`, lift it to an `and`
do_new(op, left, right)
end
defp simplify?(%NotEq{} = left, %In{} = right), do: simplify?(right, left)
defp simplify?(%NotEq{} = left, %Eq{} = right), do: simplify?(right, left)
defp simplify?(%Eq{} = left, %In{} = right), do: simplify?(right, left)
defp simplify?(%Eq{right: %Ref{}}, _), do: false
defp simplify?(_, %Eq{right: %Ref{}}), do: false
defp simplify?(%Eq{left: left, right: left_right}, %Eq{left: left, right: right_right}) do
can_optimize?(left_right) && can_optimize?(right_right)
end
defp simplify?(%NotEq{right: %Ref{}}, _), do: false
defp simplify?(_, %NotEq{right: %Ref{}}), do: false
defp simplify?(%NotEq{left: left, right: left_right}, %NotEq{left: left, right: right_right}) do
can_optimize?(left_right) && can_optimize?(right_right)
end
defp simplify?(
%Eq{left: left, right: left_right},
%In{left: left, right: %MapSet{}}
) do
can_optimize?(left_right)
end
defp simplify?(
%NotEq{left: left, right: left_right},
%In{left: left, right: %MapSet{}}
) do
can_optimize?(left_right)
end
defp simplify?(
%Eq{left: left, right: left_right},
%NotEq{left: left, right: %MapSet{}}
) do
can_optimize?(left_right)
end
defp simplify?(_, _), do: false
defp can_optimize?(value) when is_list(value) do
Enum.all?(value, &can_optimize?/1)
end
defp can_optimize?(map) when is_map(map) and not is_struct(map) do
Enum.all?(map, fn {key, val} ->
can_optimize?(key) && can_optimize?(val)
end)
end
defp can_optimize?(%{__struct__: MapSet} = mapset) do
Enum.all?(mapset, &can_optimize?/1)
end
defp can_optimize?(value) when is_integer(value) or is_binary(value) do
true
end
defp can_optimize?(_), do: false
defp do_new(op, left, right) do
if left == right do
left
else
%__MODULE__{
op: op,
left: left,
right: right
}
end
end
end
defimpl Inspect, for: Ash.Query.BooleanExpression do
import Inspect.Algebra
def inspect(
%{left: left, right: right, op: op},
opts
) do
container_type = container_type(opts)
opts = put_container_type(opts, op)
if container_type && op != container_type do
concat(["(", to_doc(left, opts), " ", to_string(op), " ", to_doc(right, opts), ")"])
else
concat([to_doc(left, opts), " ", to_string(op), " ", to_doc(right, opts)])
end
end
# custom options not available before Elixir 1.9
defp container_type(%{custom_options: options}), do: options[:container_type]
defp container_type(_), do: nil
defp put_container_type(opts, container_type) do
custom_options = apply(Map, :get, [opts, :custom_options])
apply(Map, :put, [
opts,
:custom_options,
Keyword.put(custom_options, :container_type, container_type)
])
# above version required to avoid dialyzer warnings on lack of custom_options in pre-1.9 elixir
# %{opts | custom_options: Keyword.put(opts.custom_options, :container_type, container_type)}
end
end