Skip to content

Instantly share code, notes, and snippets.

@rockneurotiko
Created March 27, 2018 09:18
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rockneurotiko/c30c43b4a3485034037663e83094fb6e to your computer and use it in GitHub Desktop.
Save rockneurotiko/c30c43b4a3485034037663e83094fb6e to your computer and use it in GitHub Desktop.
defmodule StrongParams do
def extract(params, allowed_fields, atomize \\ false)
def extract(params, allowed_fields, atomize) when not is_list(allowed_fields),
do: extract(params, [allowed_fields], atomize)
def extract(params, allowed_fields, atomize)
when is_map(params) and is_list(allowed_fields) do
Enum.reduce(allowed_fields, %{}, fn field, acc ->
case extract_field(field, params, atomize) do
nil -> acc
data -> deep_merge(acc, data)
end
end)
end
defp extract_field([], data, _), do: data
defp extract_field([field | fields], params, atomize) do
with true <- Map.has_key?(params, field),
data when not is_nil(data) <- extract_field(fields, params[field], atomize) do
field_name = maybe_atomize(atomize, field)
%{field_name => data}
else
_ -> nil
end
end
defp extract_field(field, params, atomize) do
case Map.has_key?(params, field) do
true ->
field_name = maybe_atomize(atomize, field)
%{field_name => params[field]}
_ ->
nil
end
end
defp maybe_atomize(false, result), do: result
defp maybe_atomize(true, result) when is_binary(result), do: String.to_existing_atom(result)
defp maybe_atomize(:force, result) when is_binary(result), do: String.to_atom(result)
defp maybe_atomize(_, result), do: result
defp deep_merge(original, override), do: resolve(nil, original, override)
defp resolve(_key, original, override) when is_map(original) and is_map(override) do
Map.merge(original, override, &resolve/3)
end
defp resolve(_key, _original, override), do: override
end
@rockneurotiko
Copy link
Author

Examples:

iex(26)> StrongParams.extract(%{"not_exist_atom" => 3}, "not_exist_atom")
%{"not_exist_atom" => 3}

iex(27)> StrongParams.extract(%{"not_exist_atom" => 3}, "not_exist_atom", true)
** (ArgumentError) argument error
    :erlang.binary_to_existing_atom("not_exist_atom", :utf8)
    (telex) lib/examples/test.ex:41: StrongParams.maybe_atomize/2
    (telex) lib/examples/test.ex:32: StrongParams.extract_field/3
    (telex) lib/examples/test.ex:10: anonymous fn/4 in StrongParams.extract/3
    (elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3

iex(27)> StrongParams.extract(%{"not_exist_atom" => 3}, "not_exist_atom", :force)
%{not_exist_atom: 3}

iex(28)> StrongParams.extract(%{"not_exist_atom" => 3}, "not_exist_atom", true)
%{not_exist_atom: 3}
iex(40)> params = %{"user_id" => 1, "page_size" => 20, "bogus" => "param"}
%{"bogus" => "param", "page_size" => 20, "user_id" => 1}

iex(41)> StrongParams.extract(params, ["user_id", "page_size"], :force)
%{page_size: 20, user_id: 1}
iex(46)> params = %{"I_am" => %{"Very" => "Nested", "I_m_not" => "Nested"}}
%{"I_am" => %{"I_m_not" => "Nested", "Very" => "Nested"}}

iex(47)> StrongParams.extract(params, [["I_am", "Very"]], :force)
%{I_am: %{Very: "Nested"}}
iex(49)> params = %{"a" => 123, "b" => %{"c" => 43, "d" => %{"e" => 32, "f" => 23}}}
%{"a" => 123, "b" => %{"c" => 43, "d" => %{"e" => 32, "f" => 23}}}

iex(50)> StrongParams.extract(params, ["a", ["b", "c"], ["b", "d", "e"]], :force)
%{a: 123, b: %{c: 43, d: %{e: 32}}}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment