Skip to content

Commit 061e841

Browse files
committed
initial version for atomic_map, works just with maps
0 parents  commit 061e841

7 files changed

Lines changed: 138 additions & 0 deletions

File tree

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/_build
2+
/cover
3+
/deps
4+
erl_crash.dump
5+
*.ez

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# AtomicMap
2+
3+
A small helper to convert deep hashes with mixed string/atom keys to atom-only keyed maps. Optionally with a safe option, to prevent [atom space exhaustion of the Erlang VM](https://erlangcentral.org/wiki/index.php?title=String_Conversion_To_Atom).
4+
5+
## Usage
6+
7+
8+
```elixir
9+
iex> AtomicMap.convert(%{"a" => 2, "b" => %{"c" => 4}}, safe: true)
10+
%{a: 2, b: %{c: 4}}
11+
```
12+
13+
14+
15+
16+
## Installation
17+
18+
If [available in Hex](https://hex.pm/docs/publish), the package can be installed as:
19+
20+
1. Add atomic_map to your list of dependencies in `mix.exs`:
21+
22+
def deps do
23+
[{:atomic_map, "~> 0.0.1"}]
24+
end
25+
26+
2. Ensure atomic_map is started before your application:
27+
28+
def application do
29+
[applications: [:atomic_map]]
30+
end
31+

config/config.exs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This file is responsible for configuring your application
2+
# and its dependencies with the aid of the Mix.Config module.
3+
use Mix.Config
4+
5+
# This configuration is loaded before any dependency and is restricted
6+
# to this project. If another project depends on this project, this
7+
# file won't be loaded nor affect the parent project. For this reason,
8+
# if you want to provide default values for your application for
9+
# 3rd-party users, it should be done in your "mix.exs" file.
10+
11+
# You can configure for your application as:
12+
#
13+
# config :atomic_map, key: :value
14+
#
15+
# And access this configuration in your application as:
16+
#
17+
# Application.get_env(:atomic_map, :key)
18+
#
19+
# Or configure a 3rd-party app:
20+
#
21+
# config :logger, level: :info
22+
#
23+
24+
# It is also possible to import configuration files, relative to this
25+
# directory. For example, you can emulate configuration per environment
26+
# by uncommenting the line below and defining dev.exs, test.exs and such.
27+
# Configuration from the imported file will override the ones defined
28+
# here (which is why it is important to import them last).
29+
#
30+
# import_config "#{Mix.env}.exs"

lib/atomic_map.ex

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
defmodule AtomicMap do
2+
def convert(map, opts) when is_map(map) do
3+
safe = Keyword.get(opts, :safe, true)
4+
map |> Enum.reduce(%{}, fn
5+
# convert strings to atoms
6+
{k,v}, acc when is_binary(k) and is_map(v)
7+
-> Map.put(acc, as_atom(k, safe), convert(v, opts));
8+
{k,v}, acc when is_map(v)
9+
-> Map.put(acc, k, convert(v, opts));
10+
{k,v}, acc when is_binary(k)
11+
-> Map.put(acc, as_atom(k, safe), v);
12+
{k,v}, acc
13+
-> Map.put(acc, k, v)
14+
end)
15+
end
16+
17+
defp as_atom(s, true) when is_binary(s), do: s |> String.to_existing_atom
18+
defp as_atom(s, true), do: s
19+
20+
defp as_atom(s, false) when is_binary(s), do: s |> String.to_atom
21+
defp as_atom(s, false), do: s
22+
end

mix.exs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule AtomicMap.Mixfile do
2+
use Mix.Project
3+
4+
def project do
5+
[app: :atomic_map,
6+
version: "0.0.1",
7+
elixir: "~> 1.2",
8+
build_embedded: Mix.env == :prod,
9+
start_permanent: Mix.env == :prod,
10+
deps: deps]
11+
end
12+
13+
# Configuration for the OTP application
14+
#
15+
# Type "mix help compile.app" for more information
16+
def application do
17+
[applications: [:logger]]
18+
end
19+
20+
# Dependencies can be Hex packages:
21+
#
22+
# {:mydep, "~> 0.3.0"}
23+
#
24+
# Or git/path repositories:
25+
#
26+
# {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
27+
#
28+
# Type "mix help deps" for more examples and options
29+
defp deps do
30+
[]
31+
end
32+
end

test/atomic_map_test.exs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
defmodule AtomicMapTest do
2+
use ExUnit.Case
3+
doctest AtomicMap
4+
5+
test "works with maps" do
6+
input = %{"a" => 2, "b" => %{"c" => 4}}
7+
expected = %{a: 2, b: %{c: 4}}
8+
assert AtomicMap.convert(input, safe: true) == expected
9+
end
10+
11+
test "raises for not existing atoms" do
12+
assert_raise ArgumentError, fn ->
13+
input = %{"a" => 2, "b" => %{"c" => 4}, "__not___existing__" => 5}
14+
AtomicMap.convert(input, safe: true)
15+
end
16+
end
17+
end

test/test_helper.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ExUnit.start()

0 commit comments

Comments
 (0)