Skip to content

Commit abd64c3

Browse files
committed
implement "underscore" option
problem: - as requested in #2, it would be great to provide more transformation for map keys solution: - split key conversion into multiple functions for atom conversion + underscore conversion - make options a struct - convert maps + keyword lists in opts to AtomicMap.Opts struct - keep backwards compatibility (a nice bonus!)
1 parent d233074 commit abd64c3

2 files changed

Lines changed: 40 additions & 13 deletions

File tree

lib/atomic_map.ex

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,50 @@
1+
defmodule AtomicMap.Opts do
2+
@moduledoc ~S"""
3+
Set any value to `false` to disable checking for that kind of key.
4+
"""
5+
defstruct safe: true,
6+
underscore: true
7+
end
8+
19
defmodule AtomicMap do
10+
def convert(v, opts \\ %{})
211

3-
def convert(v, opts \\ [])
4-
def convert(struct=%{__struct__: type}, opts) do
12+
def convert(struct=%{__struct__: type}, opts=%AtomicMap.Opts{}) do
513
struct
614
|> Map.from_struct
715
|> convert(opts)
816
|> Map.put(:__struct__, type)
917
end
10-
def convert(map, opts) when is_map(map) do
11-
safe = Keyword.get(opts, :safe, true)
18+
def convert(map, opts=%AtomicMap.Opts{}) when is_map(map) do
1219
map |> Enum.reduce(%{}, fn({k,v}, acc)->
13-
k = as_atom(k, safe)
14-
v = convert(v, opts)
20+
k = k |> convert_key(opts)
21+
v = v |> convert(opts)
1522
acc |> Map.put(k, v)
1623
end)
1724
end
18-
def convert(list, opts) when is_list(list) do
25+
def convert(list, opts=%AtomicMap.Opts{}) when is_list(list) do
1926
list |> Enum.map(fn(x)-> convert(x, opts) end)
2027
end
21-
def convert(tuple, opts) when is_tuple(tuple) do
28+
def convert(tuple, opts=%AtomicMap.Opts{}) when is_tuple(tuple) do
2229
tuple |> Tuple.to_list |> convert(opts) |> List.to_tuple
2330
end
24-
def convert(v, _opts), do: v
31+
def convert(v, _opts=%AtomicMap.Opts{}), do: v
32+
33+
# if you pass a plain map or keyword list as opts, those will match and convert it to struct
34+
def convert(v, opts=%{}), do: convert(v, struct(AtomicMap.Opts, opts))
35+
def convert(v, opts) when is_list(opts), do: convert(v, Enum.into(opts, %{}))
36+
37+
defp convert_key(k, opts) do
38+
k
39+
|> as_underscore(opts.underscore)
40+
|> as_atom(opts.safe)
41+
end
42+
43+
defp as_atom(s, true) when is_binary(s), do: s |> String.to_existing_atom
44+
defp as_atom(s, false) when is_binary(s), do: s |> String.to_atom
45+
defp as_atom(s, _), do: s
2546

26-
defp as_atom(s, true) when is_binary(s), do: s |> String.to_existing_atom
27-
defp as_atom(s, false) when is_binary(s), do: s |> String.to_atom
28-
defp as_atom(s, _), do: s
47+
def as_underscore(s, true) when is_binary(s), do: s |> Macro.underscore
48+
def as_underscore(s, true) when is_atom(s), do: s |> Atom.to_string |> as_underscore(true)
49+
def as_underscore(s, false), do: s
2950
end

test/atomic_map_test.exs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ defmodule AtomicMapTest do
99
test "works with maps" do
1010
input = %{"a" => 2, "b" => %{"c" => 4}}
1111
expected = %{a: 2, b: %{c: 4}}
12-
assert AtomicMap.convert(input, safe: true) == expected
12+
assert AtomicMap.convert(input, %{safe: true}) == expected
1313
end
1414

1515
test "works with maps with lists" do
@@ -60,6 +60,12 @@ defmodule AtomicMapTest do
6060
assert AtomicMap.convert(input) == expected
6161
end
6262

63+
test "convertes keys to underscore by default (attention: safe: false needed here)" do
64+
input = %{ "firstKey" => {1,2}, :secondKey => 4}
65+
expected = %{first_key: {1, 2}, second_key: 4}
66+
assert AtomicMap.convert(input, safe: false) == expected
67+
end
68+
6369
test "raises for not existing atoms" do
6470
assert_raise ArgumentError, fn ->
6571
input = %{"a" => 2, "b" => %{"c" => 4}, "__not___existing__" => 5}

0 commit comments

Comments
 (0)