项目作者: ruby2elixir

项目描述 :
A small utility to convert deep Elixir maps with mixed string/atom keys to atom-only keyed maps.
高级语言: Elixir
项目地址: git://github.com/ruby2elixir/atomic_map.git
创建时间: 2016-04-19T13:28:13Z
项目社区:https://github.com/ruby2elixir/atomic_map

开源协议:

下载


AtomicMap

Build status
Hex version
Hex downloads

A small utility to convert Elixir maps with mixed string/atom keys to atom-only keyed maps. Optionally with a safe option, to prevent atom space exhaustion of the Erlang VM. Since v0.8 it also supports conversion of keys from CamelCase to under_score format.

AtomicMap can convert simple maps or nested structures such as lists of maps; see Nested Structures below for examples.

Usage

Safety

With no options, it safely converts string keys in maps to atoms, using String.to_existing_atom/1.

  1. iex> AtomicMap.convert(%{"a" => "a", "b" => "b", c: "c"})
  2. %{a: "a", b: "b", c: "c"}

Because safe conversion uses String.to_existing_atom/1, it will raise when the target atom does not exist.

  1. iex> AtomicMap.convert(%{"abcdefg" => "a", "b" => "b"})
  2. ** (ArgumentError) argument error
  3. :erlang.binary_to_existing_atom("abcdefg", :utf8)

To have safe conversion can ignore unsafe keys, leaving them as strings, pass true for the ignore option.

  1. iex> AtomicMap.convert(%{"abcdefg" => "a", "b" => "b"}, %{ignore: true})
  2. %{:b => "b", "abcdefg" => "a"}

To disable safe conversion and allow new atoms to be created, pass false for the safe: option.
(This makes the ignore option irrelevant.)
If the input is user-generated, converting only expected keys will prevent excessive atom creation.

  1. iex> map = %{"expected_key" => "a", "b" => "b", "unexpected_key" => "c"}
  2. %{"expected_key" => "a", "b" => "b", "unexpected_key" => "c"}
  3. iex> filtered_map = Map.take(map, ["expected_key", "b"])
  4. %{"b" => "b", "expected_key" => "a"}
  5. iex> AtomicMap.convert(filtered_map, %{safe: false})
  6. %{b: "b", expected_key: "a"}

Underscoring

By default, "CamelCase" string keys will be converted to under_score atom keys.

  1. iex> AtomicMap.convert(%{ "CamelCase" => "hi" })
  2. ** (ArgumentError) argument error
  3. :erlang.binary_to_existing_atom("camel_case", :utf8)

Note that "camel_case" was the string that failed conversion.
If that atom is explicitly created first, the conversion will succeed.

  1. iex> :camel_case
  2. :camel_case
  3. iex> AtomicMap.convert(%{ "CamelCase" => "hi" })
  4. %{camel_case: "hi"}

Allowing unsafe conversions will also work.
If the input is user-generated, converting only expected keys will prevent excessive atom creation.

  1. iex> map = %{"CamelCase" => "a", "b" => "b", "AnotherCamelCase" => "c"}
  2. %{"CamelCase" => "a", "b" => "b", "AnotherCamelCase" => "c"}
  3. iex> filtered_map = Map.take(map, ["CamelCase", "b"])
  4. %{"b" => "b", "CamelCase" => "a"}
  5. iex> AtomicMap.convert(filtered_map, %{safe: false})
  6. %{b: "b", camel_case: "a"}

Replacing Hyphens

"hyphenated-string" keys will always be converted to under_score atom keys.
There is currently no way to disable this behavior.

  1. iex> AtomicMap.convert(%{"some-key" => "a", "b" => "c"})
  2. ** (ArgumentError) argument error
  3. :erlang.binary_to_existing_atom("some_key", :utf8)

Note that "some_key" was the string that failed conversion.
If that atom is explicitly created first, the conversion will succeed.

  1. iex> :some_key
  2. :some_key
  3. iex> AtomicMap.convert(%{"some-key" => "a", "b" => "c"})
  4. %{b: "c", some_key: "a"}

Allowing unsafe conversions will also work.
If the input is user-generated, converting only expected keys will prevent excessive atom creation.

  1. iex> map = %{"some-key" => "a", "b" => "b", "another-key" => "c"}
  2. %{"some-key" => "a", "b" => "b", "another-key" => "c"}
  3. iex> filtered_map = Map.take(map, ["some-key", "b"])
  4. %{"b" => "b", "some-key" => "a"}
  5. iex> AtomicMap.convert(filtered_map, %{safe: false})
  6. %{b: "b", some_key: "a"}

Nested Structures

  1. # works with nested maps
  2. iex> AtomicMap.convert(%{"a" => 2, "b" => %{"c" => 4}})
  3. %{a: 2, b: %{c: 4}}
  4. # works with nested maps + lists + mixed key types (atoms + binaries)
  5. iex> AtomicMap.convert([ %{"c" => 1}, %{:c => 2}, %{"c" => %{:b => 4}}], %{safe: true})
  6. [%{c: 1}, %{c: 2}, %{c: %{b: 4}}]

Installation

  1. Add atomic_map to your list of dependencies in mix.exs:

    def deps do

    1. [{:atomic_map, "~> 0.8"}]

    end

Todo:

  • maybe allow direct conversion to a struct, like Poison does it: as: %SomeStruct{}…

Benchmark

  1. $ mix bench