Latest commit

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Hex Version

NiceMaps

NiceMaps provides a single function parse to convert maps into the desired format.

It can build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

🔥 Danger Zone 🔥

NiceMaps uses String.to_existing_atom/1 for conversions from string keys to atom keys and camelcase-snake_case, so please make sure your atoms exists before attempting something like

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase)# ** (ArgumentError) argument error# :erlang.binary_to_existing_atom("thisDoesNotExistAsCamelcase", :utf8)

If you need to convert a map with unknown atoms, please use string keys instead:

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase,key_type: :string)# %{"thisDoesNotExistAsCamelcase" => "abc"}# or%{"this_does_not_exist_as_camelcase"=>"abc"}|>NiceMaps.parse(keys: :camelcase)

NiceMaps does not provide a key_type: :atom option for the same reason explained later, but you can convert keys to existing atoms using key_type: :existing_atom. If you absolutely insist on creating unknown atoms, there is a way to do it, but I will leave it to you to figure it out from the code (because I think it is a bad idea, and you should really know what you are doing before using it.)

How to use it, and what for

Many people prefer working with atom keys over string keys, because you get some nice syntactic sugar like map.key and the JSON like notation %{key: "value"}, but because atoms are not garbage collected, web frameworks provide parameters as string key maps (otherwise an attacker could flood your memory with atoms until your server crashes.)

So, lets say you have parameters in a Phoenix controller and you want to convert the keys into atoms:

# We only allow these keys, you could call it the "strong parameters approach"@allowed_keys["a","b","c"]defmy_controller(conn,params)doparams|>Map.take(@allowed_keys)|>NiceMaps.parse(key_type: :existing_atom)|>MyContext.create_a_thing()end

Another possible use case is JSON parsing. If you have a map that could or could not have struct values, libraries like Jason will explode on you, and you have to implement the Jason.Encoder protocol for your struct - which is not possible if you do not have control over the structs. NiceMaps to the rescue:

converted=%MyStruct{a: "a",b: "b",a_struct: %MyOtherStruct{c: "c"}}|>NiceMaps.parse(convert_structs: true)# {a: "a", b: "b", a_struct: %{c: "c"}}Jason.encode!(converted)

Last but not least, converting keys from snake case (with_underscore) to camelcase (likeThis) and vice versa. Different protocols/frameworks/programing languages use different conventions, snake case vs camelcase is one of those where there is no right or wrong, but you might want to convert - for example - a graphql response to a more "elixiry" map (using Neuron for this example):

{:ok,%{body: response}}=Neuron.query("""{ aThing { aField }}""")NiceMaps.parse(response,keys: :snake_case)# %{a_thing: %{a_field: "whatever"}}

Options

  • :keys one of :camelcase or :snake_case
  • :convert_structs one of true or false, default: false
  • :key_type, one of :string or :existing_atom

Examples

Without Options:

iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "bar"})
%{id: 1, my_key: "bar"}
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}])
[%{id: 1, my_key: "bar"}, %{value: "a"}]
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, "String"])
[%{id: 1, my_key: "bar"}, "String"]
iex> NiceMaps.parse(%{0 => "0", 1 => "1"})
%{0 => "0", 1 => "1"}

Keys to camelcase:

iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}], keys: :camelcase)
[%{id: 1, myKey: "bar"}, %{value: "a"}]
iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "foo"}, keys: :camelcase)
%{id: 1, myKey: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}
# Keys to snake case:
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}

Convert all structs into maps

iex> map = %{
...> list: [
...> %MyStruct{id: 1, my_key: "foo"}
...> ],
...> struct: %MyStruct{id: 2, my_key: "bar"},
...> other_struct: %MyStruct{id: 3, my_key: %MyStruct{id: 4, my_key: nil}}
...> }
...> NiceMaps.parse(map, convert_structs: true)
%{
list: [
%{id: 1, my_key: "foo"}
],
struct: %{id: 2, my_key: "bar"},
other_struct: %{id: 3, my_key: %{id: 4, my_key: nil}}
}

Convert string keys to existing atom

iex> map = %{
...> "key1" => "value 1",
...> "nested" => %{"key2" => "value 2"},
...> "list" => [%{"key3" => "value 3", "key4" => "value 4"}],
...> 1 => "an integer key",
...> %MyStruct{} => "a struct key"
...> }
iex> [:key1, :key2, :key3, :key4, :nested, :list] # Make sure atoms exist
iex> NiceMaps.parse(map, key_type: :existing_atom)
%{
:key1 => "value 1",
:nested => %{key2: "value 2"},
:list => [%{key3: "value 3", key4: "value 4"}],
1 => "an integer key",
%MyStruct{} => "a struct key"
}

Mix it all together

iex> map = %{
...> "hello_there" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> NiceMaps.parse(map, keys: :camelcase, key_type: :string)
%{"helloThere" => [%{"aA" => "asdf"}, %{"aA" => "bhjk"}, "a string", 1], "thingA" => "thing A", "thingB" => "thing B"}
iex> map = %{
...> "helloThere" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> [:hello_there, :thing_a, :thing_b] # make sure atoms exist
iex> NiceMaps.parse(map, keys: :snake_case, key_type: :existing_atom)
%{:hello_there => [%{:a_a => "asdf"}, %{:a_a => "bhjk"}, "a string", 1], :thing_a => "thing A", :thing_b => "thing B"}

Installation

If available in Hex, the package can be installed by adding nice_maps to your list of dependencies in mix.exs:

defdepsdo[{:nice_maps,"~> 0.1.0"}]end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/nice_maps.

About

Build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Hex Version

NiceMaps

NiceMaps provides a single function parse to convert maps into the desired format.

It can build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

🔥 Danger Zone 🔥

NiceMaps uses String.to_existing_atom/1 for conversions from string keys to atom keys and camelcase-snake_case, so please make sure your atoms exists before attempting something like

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase)# ** (ArgumentError) argument error# :erlang.binary_to_existing_atom("thisDoesNotExistAsCamelcase", :utf8)

If you need to convert a map with unknown atoms, please use string keys instead:

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase,key_type: :string)# %{"thisDoesNotExistAsCamelcase" => "abc"}# or%{"this_does_not_exist_as_camelcase"=>"abc"}|>NiceMaps.parse(keys: :camelcase)

NiceMaps does not provide a key_type: :atom option for the same reason explained later, but you can convert keys to existing atoms using key_type: :existing_atom. If you absolutely insist on creating unknown atoms, there is a way to do it, but I will leave it to you to figure it out from the code (because I think it is a bad idea, and you should really know what you are doing before using it.)

How to use it, and what for

Many people prefer working with atom keys over string keys, because you get some nice syntactic sugar like map.key and the JSON like notation %{key: "value"}, but because atoms are not garbage collected, web frameworks provide parameters as string key maps (otherwise an attacker could flood your memory with atoms until your server crashes.)

So, lets say you have parameters in a Phoenix controller and you want to convert the keys into atoms:

# We only allow these keys, you could call it the "strong parameters approach"@allowed_keys["a","b","c"]defmy_controller(conn,params)doparams|>Map.take(@allowed_keys)|>NiceMaps.parse(key_type: :existing_atom)|>MyContext.create_a_thing()end

Another possible use case is JSON parsing. If you have a map that could or could not have struct values, libraries like Jason will explode on you, and you have to implement the Jason.Encoder protocol for your struct - which is not possible if you do not have control over the structs. NiceMaps to the rescue:

converted=%MyStruct{a: "a",b: "b",a_struct: %MyOtherStruct{c: "c"}}|>NiceMaps.parse(convert_structs: true)# {a: "a", b: "b", a_struct: %{c: "c"}}Jason.encode!(converted)

Last but not least, converting keys from snake case (with_underscore) to camelcase (likeThis) and vice versa. Different protocols/frameworks/programing languages use different conventions, snake case vs camelcase is one of those where there is no right or wrong, but you might want to convert - for example - a graphql response to a more "elixiry" map (using Neuron for this example):

{:ok,%{body: response}}=Neuron.query("""{ aThing { aField }}""")NiceMaps.parse(response,keys: :snake_case)# %{a_thing: %{a_field: "whatever"}}

Options

  • :keys one of :camelcase or :snake_case
  • :convert_structs one of true or false, default: false
  • :key_type, one of :string or :existing_atom

Examples

Without Options:

iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "bar"})
%{id: 1, my_key: "bar"}
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}])
[%{id: 1, my_key: "bar"}, %{value: "a"}]
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, "String"])
[%{id: 1, my_key: "bar"}, "String"]
iex> NiceMaps.parse(%{0 => "0", 1 => "1"})
%{0 => "0", 1 => "1"}

Keys to camelcase:

iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}], keys: :camelcase)
[%{id: 1, myKey: "bar"}, %{value: "a"}]
iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "foo"}, keys: :camelcase)
%{id: 1, myKey: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}
# Keys to snake case:
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}

Convert all structs into maps

iex> map = %{
...> list: [
...> %MyStruct{id: 1, my_key: "foo"}
...> ],
...> struct: %MyStruct{id: 2, my_key: "bar"},
...> other_struct: %MyStruct{id: 3, my_key: %MyStruct{id: 4, my_key: nil}}
...> }
...> NiceMaps.parse(map, convert_structs: true)
%{
list: [
%{id: 1, my_key: "foo"}
],
struct: %{id: 2, my_key: "bar"},
other_struct: %{id: 3, my_key: %{id: 4, my_key: nil}}
}

Convert string keys to existing atom

iex> map = %{
...> "key1" => "value 1",
...> "nested" => %{"key2" => "value 2"},
...> "list" => [%{"key3" => "value 3", "key4" => "value 4"}],
...> 1 => "an integer key",
...> %MyStruct{} => "a struct key"
...> }
iex> [:key1, :key2, :key3, :key4, :nested, :list] # Make sure atoms exist
iex> NiceMaps.parse(map, key_type: :existing_atom)
%{
:key1 => "value 1",
:nested => %{key2: "value 2"},
:list => [%{key3: "value 3", key4: "value 4"}],
1 => "an integer key",
%MyStruct{} => "a struct key"
}

Mix it all together

iex> map = %{
...> "hello_there" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> NiceMaps.parse(map, keys: :camelcase, key_type: :string)
%{"helloThere" => [%{"aA" => "asdf"}, %{"aA" => "bhjk"}, "a string", 1], "thingA" => "thing A", "thingB" => "thing B"}
iex> map = %{
...> "helloThere" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> [:hello_there, :thing_a, :thing_b] # make sure atoms exist
iex> NiceMaps.parse(map, keys: :snake_case, key_type: :existing_atom)
%{:hello_there => [%{:a_a => "asdf"}, %{:a_a => "bhjk"}, "a string", 1], :thing_a => "thing A", :thing_b => "thing B"}

Installation

If available in Hex, the package can be installed by adding nice_maps to your list of dependencies in mix.exs:

defdepsdo[{:nice_maps,"~> 0.1.0"}]end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/nice_maps.

About

Build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Hex Version

NiceMaps

NiceMaps provides a single function parse to convert maps into the desired format.

It can build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

🔥 Danger Zone 🔥

NiceMaps uses String.to_existing_atom/1 for conversions from string keys to atom keys and camelcase-snake_case, so please make sure your atoms exists before attempting something like

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase)# ** (ArgumentError) argument error# :erlang.binary_to_existing_atom("thisDoesNotExistAsCamelcase", :utf8)

If you need to convert a map with unknown atoms, please use string keys instead:

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase,key_type: :string)# %{"thisDoesNotExistAsCamelcase" => "abc"}# or%{"this_does_not_exist_as_camelcase"=>"abc"}|>NiceMaps.parse(keys: :camelcase)

NiceMaps does not provide a key_type: :atom option for the same reason explained later, but you can convert keys to existing atoms using key_type: :existing_atom. If you absolutely insist on creating unknown atoms, there is a way to do it, but I will leave it to you to figure it out from the code (because I think it is a bad idea, and you should really know what you are doing before using it.)

How to use it, and what for

Many people prefer working with atom keys over string keys, because you get some nice syntactic sugar like map.key and the JSON like notation %{key: "value"}, but because atoms are not garbage collected, web frameworks provide parameters as string key maps (otherwise an attacker could flood your memory with atoms until your server crashes.)

So, lets say you have parameters in a Phoenix controller and you want to convert the keys into atoms:

# We only allow these keys, you could call it the "strong parameters approach"@allowed_keys["a","b","c"]defmy_controller(conn,params)doparams|>Map.take(@allowed_keys)|>NiceMaps.parse(key_type: :existing_atom)|>MyContext.create_a_thing()end

Another possible use case is JSON parsing. If you have a map that could or could not have struct values, libraries like Jason will explode on you, and you have to implement the Jason.Encoder protocol for your struct - which is not possible if you do not have control over the structs. NiceMaps to the rescue:

converted=%MyStruct{a: "a",b: "b",a_struct: %MyOtherStruct{c: "c"}}|>NiceMaps.parse(convert_structs: true)# {a: "a", b: "b", a_struct: %{c: "c"}}Jason.encode!(converted)

Last but not least, converting keys from snake case (with_underscore) to camelcase (likeThis) and vice versa. Different protocols/frameworks/programing languages use different conventions, snake case vs camelcase is one of those where there is no right or wrong, but you might want to convert - for example - a graphql response to a more "elixiry" map (using Neuron for this example):

{:ok,%{body: response}}=Neuron.query("""{ aThing { aField }}""")NiceMaps.parse(response,keys: :snake_case)# %{a_thing: %{a_field: "whatever"}}

Options

  • :keys one of :camelcase or :snake_case
  • :convert_structs one of true or false, default: false
  • :key_type, one of :string or :existing_atom

Examples

Without Options:

iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "bar"})
%{id: 1, my_key: "bar"}
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}])
[%{id: 1, my_key: "bar"}, %{value: "a"}]
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, "String"])
[%{id: 1, my_key: "bar"}, "String"]
iex> NiceMaps.parse(%{0 => "0", 1 => "1"})
%{0 => "0", 1 => "1"}

Keys to camelcase:

iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}], keys: :camelcase)
[%{id: 1, myKey: "bar"}, %{value: "a"}]
iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "foo"}, keys: :camelcase)
%{id: 1, myKey: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}
# Keys to snake case:
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}

Convert all structs into maps

iex> map = %{
...> list: [
...> %MyStruct{id: 1, my_key: "foo"}
...> ],
...> struct: %MyStruct{id: 2, my_key: "bar"},
...> other_struct: %MyStruct{id: 3, my_key: %MyStruct{id: 4, my_key: nil}}
...> }
...> NiceMaps.parse(map, convert_structs: true)
%{
list: [
%{id: 1, my_key: "foo"}
],
struct: %{id: 2, my_key: "bar"},
other_struct: %{id: 3, my_key: %{id: 4, my_key: nil}}
}

Convert string keys to existing atom

iex> map = %{
...> "key1" => "value 1",
...> "nested" => %{"key2" => "value 2"},
...> "list" => [%{"key3" => "value 3", "key4" => "value 4"}],
...> 1 => "an integer key",
...> %MyStruct{} => "a struct key"
...> }
iex> [:key1, :key2, :key3, :key4, :nested, :list] # Make sure atoms exist
iex> NiceMaps.parse(map, key_type: :existing_atom)
%{
:key1 => "value 1",
:nested => %{key2: "value 2"},
:list => [%{key3: "value 3", key4: "value 4"}],
1 => "an integer key",
%MyStruct{} => "a struct key"
}

Mix it all together

iex> map = %{
...> "hello_there" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> NiceMaps.parse(map, keys: :camelcase, key_type: :string)
%{"helloThere" => [%{"aA" => "asdf"}, %{"aA" => "bhjk"}, "a string", 1], "thingA" => "thing A", "thingB" => "thing B"}
iex> map = %{
...> "helloThere" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> [:hello_there, :thing_a, :thing_b] # make sure atoms exist
iex> NiceMaps.parse(map, keys: :snake_case, key_type: :existing_atom)
%{:hello_there => [%{:a_a => "asdf"}, %{:a_a => "bhjk"}, "a string", 1], :thing_a => "thing A", :thing_b => "thing B"}

Installation

If available in Hex, the package can be installed by adding nice_maps to your list of dependencies in mix.exs:

defdepsdo[{:nice_maps,"~> 0.1.0"}]end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/nice_maps.

About

Build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Hex Version

NiceMaps

NiceMaps provides a single function parse to convert maps into the desired format.

It can build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

🔥 Danger Zone 🔥

NiceMaps uses String.to_existing_atom/1 for conversions from string keys to atom keys and camelcase-snake_case, so please make sure your atoms exists before attempting something like

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase)# ** (ArgumentError) argument error# :erlang.binary_to_existing_atom("thisDoesNotExistAsCamelcase", :utf8)

If you need to convert a map with unknown atoms, please use string keys instead:

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase,key_type: :string)# %{"thisDoesNotExistAsCamelcase" => "abc"}# or%{"this_does_not_exist_as_camelcase"=>"abc"}|>NiceMaps.parse(keys: :camelcase)

NiceMaps does not provide a key_type: :atom option for the same reason explained later, but you can convert keys to existing atoms using key_type: :existing_atom. If you absolutely insist on creating unknown atoms, there is a way to do it, but I will leave it to you to figure it out from the code (because I think it is a bad idea, and you should really know what you are doing before using it.)

How to use it, and what for

Many people prefer working with atom keys over string keys, because you get some nice syntactic sugar like map.key and the JSON like notation %{key: "value"}, but because atoms are not garbage collected, web frameworks provide parameters as string key maps (otherwise an attacker could flood your memory with atoms until your server crashes.)

So, lets say you have parameters in a Phoenix controller and you want to convert the keys into atoms:

# We only allow these keys, you could call it the "strong parameters approach"@allowed_keys["a","b","c"]defmy_controller(conn,params)doparams|>Map.take(@allowed_keys)|>NiceMaps.parse(key_type: :existing_atom)|>MyContext.create_a_thing()end

Another possible use case is JSON parsing. If you have a map that could or could not have struct values, libraries like Jason will explode on you, and you have to implement the Jason.Encoder protocol for your struct - which is not possible if you do not have control over the structs. NiceMaps to the rescue:

converted=%MyStruct{a: "a",b: "b",a_struct: %MyOtherStruct{c: "c"}}|>NiceMaps.parse(convert_structs: true)# {a: "a", b: "b", a_struct: %{c: "c"}}Jason.encode!(converted)

Last but not least, converting keys from snake case (with_underscore) to camelcase (likeThis) and vice versa. Different protocols/frameworks/programing languages use different conventions, snake case vs camelcase is one of those where there is no right or wrong, but you might want to convert - for example - a graphql response to a more "elixiry" map (using Neuron for this example):

{:ok,%{body: response}}=Neuron.query("""{ aThing { aField }}""")NiceMaps.parse(response,keys: :snake_case)# %{a_thing: %{a_field: "whatever"}}

Options

  • :keys one of :camelcase or :snake_case
  • :convert_structs one of true or false, default: false
  • :key_type, one of :string or :existing_atom

Examples

Without Options:

iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "bar"})
%{id: 1, my_key: "bar"}
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}])
[%{id: 1, my_key: "bar"}, %{value: "a"}]
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, "String"])
[%{id: 1, my_key: "bar"}, "String"]
iex> NiceMaps.parse(%{0 => "0", 1 => "1"})
%{0 => "0", 1 => "1"}

Keys to camelcase:

iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}], keys: :camelcase)
[%{id: 1, myKey: "bar"}, %{value: "a"}]
iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "foo"}, keys: :camelcase)
%{id: 1, myKey: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}
# Keys to snake case:
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}

Convert all structs into maps

iex> map = %{
...> list: [
...> %MyStruct{id: 1, my_key: "foo"}
...> ],
...> struct: %MyStruct{id: 2, my_key: "bar"},
...> other_struct: %MyStruct{id: 3, my_key: %MyStruct{id: 4, my_key: nil}}
...> }
...> NiceMaps.parse(map, convert_structs: true)
%{
list: [
%{id: 1, my_key: "foo"}
],
struct: %{id: 2, my_key: "bar"},
other_struct: %{id: 3, my_key: %{id: 4, my_key: nil}}
}

Convert string keys to existing atom

iex> map = %{
...> "key1" => "value 1",
...> "nested" => %{"key2" => "value 2"},
...> "list" => [%{"key3" => "value 3", "key4" => "value 4"}],
...> 1 => "an integer key",
...> %MyStruct{} => "a struct key"
...> }
iex> [:key1, :key2, :key3, :key4, :nested, :list] # Make sure atoms exist
iex> NiceMaps.parse(map, key_type: :existing_atom)
%{
:key1 => "value 1",
:nested => %{key2: "value 2"},
:list => [%{key3: "value 3", key4: "value 4"}],
1 => "an integer key",
%MyStruct{} => "a struct key"
}

Mix it all together

iex> map = %{
...> "hello_there" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> NiceMaps.parse(map, keys: :camelcase, key_type: :string)
%{"helloThere" => [%{"aA" => "asdf"}, %{"aA" => "bhjk"}, "a string", 1], "thingA" => "thing A", "thingB" => "thing B"}
iex> map = %{
...> "helloThere" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> [:hello_there, :thing_a, :thing_b] # make sure atoms exist
iex> NiceMaps.parse(map, keys: :snake_case, key_type: :existing_atom)
%{:hello_there => [%{:a_a => "asdf"}, %{:a_a => "bhjk"}, "a string", 1], :thing_a => "thing A", :thing_b => "thing B"}

Installation

If available in Hex, the package can be installed by adding nice_maps to your list of dependencies in mix.exs:

defdepsdo[{:nice_maps,"~> 0.1.0"}]end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/nice_maps.

About

Build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Hex Version

NiceMaps

NiceMaps provides a single function parse to convert maps into the desired format.

It can build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

🔥 Danger Zone 🔥

NiceMaps uses String.to_existing_atom/1 for conversions from string keys to atom keys and camelcase-snake_case, so please make sure your atoms exists before attempting something like

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase)# ** (ArgumentError) argument error# :erlang.binary_to_existing_atom("thisDoesNotExistAsCamelcase", :utf8)

If you need to convert a map with unknown atoms, please use string keys instead:

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase,key_type: :string)# %{"thisDoesNotExistAsCamelcase" => "abc"}# or%{"this_does_not_exist_as_camelcase"=>"abc"}|>NiceMaps.parse(keys: :camelcase)

NiceMaps does not provide a key_type: :atom option for the same reason explained later, but you can convert keys to existing atoms using key_type: :existing_atom. If you absolutely insist on creating unknown atoms, there is a way to do it, but I will leave it to you to figure it out from the code (because I think it is a bad idea, and you should really know what you are doing before using it.)

How to use it, and what for

Many people prefer working with atom keys over string keys, because you get some nice syntactic sugar like map.key and the JSON like notation %{key: "value"}, but because atoms are not garbage collected, web frameworks provide parameters as string key maps (otherwise an attacker could flood your memory with atoms until your server crashes.)

So, lets say you have parameters in a Phoenix controller and you want to convert the keys into atoms:

# We only allow these keys, you could call it the "strong parameters approach"@allowed_keys["a","b","c"]defmy_controller(conn,params)doparams|>Map.take(@allowed_keys)|>NiceMaps.parse(key_type: :existing_atom)|>MyContext.create_a_thing()end

Another possible use case is JSON parsing. If you have a map that could or could not have struct values, libraries like Jason will explode on you, and you have to implement the Jason.Encoder protocol for your struct - which is not possible if you do not have control over the structs. NiceMaps to the rescue:

converted=%MyStruct{a: "a",b: "b",a_struct: %MyOtherStruct{c: "c"}}|>NiceMaps.parse(convert_structs: true)# {a: "a", b: "b", a_struct: %{c: "c"}}Jason.encode!(converted)

Last but not least, converting keys from snake case (with_underscore) to camelcase (likeThis) and vice versa. Different protocols/frameworks/programing languages use different conventions, snake case vs camelcase is one of those where there is no right or wrong, but you might want to convert - for example - a graphql response to a more "elixiry" map (using Neuron for this example):

{:ok,%{body: response}}=Neuron.query("""{ aThing { aField }}""")NiceMaps.parse(response,keys: :snake_case)# %{a_thing: %{a_field: "whatever"}}

Options

  • :keys one of :camelcase or :snake_case
  • :convert_structs one of true or false, default: false
  • :key_type, one of :string or :existing_atom

Examples

Without Options:

iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "bar"})
%{id: 1, my_key: "bar"}
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}])
[%{id: 1, my_key: "bar"}, %{value: "a"}]
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, "String"])
[%{id: 1, my_key: "bar"}, "String"]
iex> NiceMaps.parse(%{0 => "0", 1 => "1"})
%{0 => "0", 1 => "1"}

Keys to camelcase:

iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}], keys: :camelcase)
[%{id: 1, myKey: "bar"}, %{value: "a"}]
iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "foo"}, keys: :camelcase)
%{id: 1, myKey: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}
# Keys to snake case:
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}

Convert all structs into maps

iex> map = %{
...> list: [
...> %MyStruct{id: 1, my_key: "foo"}
...> ],
...> struct: %MyStruct{id: 2, my_key: "bar"},
...> other_struct: %MyStruct{id: 3, my_key: %MyStruct{id: 4, my_key: nil}}
...> }
...> NiceMaps.parse(map, convert_structs: true)
%{
list: [
%{id: 1, my_key: "foo"}
],
struct: %{id: 2, my_key: "bar"},
other_struct: %{id: 3, my_key: %{id: 4, my_key: nil}}
}

Convert string keys to existing atom

iex> map = %{
...> "key1" => "value 1",
...> "nested" => %{"key2" => "value 2"},
...> "list" => [%{"key3" => "value 3", "key4" => "value 4"}],
...> 1 => "an integer key",
...> %MyStruct{} => "a struct key"
...> }
iex> [:key1, :key2, :key3, :key4, :nested, :list] # Make sure atoms exist
iex> NiceMaps.parse(map, key_type: :existing_atom)
%{
:key1 => "value 1",
:nested => %{key2: "value 2"},
:list => [%{key3: "value 3", key4: "value 4"}],
1 => "an integer key",
%MyStruct{} => "a struct key"
}

Mix it all together

iex> map = %{
...> "hello_there" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> NiceMaps.parse(map, keys: :camelcase, key_type: :string)
%{"helloThere" => [%{"aA" => "asdf"}, %{"aA" => "bhjk"}, "a string", 1], "thingA" => "thing A", "thingB" => "thing B"}
iex> map = %{
...> "helloThere" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> [:hello_there, :thing_a, :thing_b] # make sure atoms exist
iex> NiceMaps.parse(map, keys: :snake_case, key_type: :existing_atom)
%{:hello_there => [%{:a_a => "asdf"}, %{:a_a => "bhjk"}, "a string", 1], :thing_a => "thing A", :thing_b => "thing B"}

Installation

If available in Hex, the package can be installed by adding nice_maps to your list of dependencies in mix.exs:

defdepsdo[{:nice_maps,"~> 0.1.0"}]end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/nice_maps.

About

Build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Hex Version

NiceMaps

NiceMaps provides a single function parse to convert maps into the desired format.

It can build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

🔥 Danger Zone 🔥

NiceMaps uses String.to_existing_atom/1 for conversions from string keys to atom keys and camelcase-snake_case, so please make sure your atoms exists before attempting something like

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase)# ** (ArgumentError) argument error# :erlang.binary_to_existing_atom("thisDoesNotExistAsCamelcase", :utf8)

If you need to convert a map with unknown atoms, please use string keys instead:

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase,key_type: :string)# %{"thisDoesNotExistAsCamelcase" => "abc"}# or%{"this_does_not_exist_as_camelcase"=>"abc"}|>NiceMaps.parse(keys: :camelcase)

NiceMaps does not provide a key_type: :atom option for the same reason explained later, but you can convert keys to existing atoms using key_type: :existing_atom. If you absolutely insist on creating unknown atoms, there is a way to do it, but I will leave it to you to figure it out from the code (because I think it is a bad idea, and you should really know what you are doing before using it.)

How to use it, and what for

Many people prefer working with atom keys over string keys, because you get some nice syntactic sugar like map.key and the JSON like notation %{key: "value"}, but because atoms are not garbage collected, web frameworks provide parameters as string key maps (otherwise an attacker could flood your memory with atoms until your server crashes.)

So, lets say you have parameters in a Phoenix controller and you want to convert the keys into atoms:

# We only allow these keys, you could call it the "strong parameters approach"@allowed_keys["a","b","c"]defmy_controller(conn,params)doparams|>Map.take(@allowed_keys)|>NiceMaps.parse(key_type: :existing_atom)|>MyContext.create_a_thing()end

Another possible use case is JSON parsing. If you have a map that could or could not have struct values, libraries like Jason will explode on you, and you have to implement the Jason.Encoder protocol for your struct - which is not possible if you do not have control over the structs. NiceMaps to the rescue:

converted=%MyStruct{a: "a",b: "b",a_struct: %MyOtherStruct{c: "c"}}|>NiceMaps.parse(convert_structs: true)# {a: "a", b: "b", a_struct: %{c: "c"}}Jason.encode!(converted)

Last but not least, converting keys from snake case (with_underscore) to camelcase (likeThis) and vice versa. Different protocols/frameworks/programing languages use different conventions, snake case vs camelcase is one of those where there is no right or wrong, but you might want to convert - for example - a graphql response to a more "elixiry" map (using Neuron for this example):

{:ok,%{body: response}}=Neuron.query("""{ aThing { aField }}""")NiceMaps.parse(response,keys: :snake_case)# %{a_thing: %{a_field: "whatever"}}

Options

  • :keys one of :camelcase or :snake_case
  • :convert_structs one of true or false, default: false
  • :key_type, one of :string or :existing_atom

Examples

Without Options:

iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "bar"})
%{id: 1, my_key: "bar"}
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}])
[%{id: 1, my_key: "bar"}, %{value: "a"}]
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, "String"])
[%{id: 1, my_key: "bar"}, "String"]
iex> NiceMaps.parse(%{0 => "0", 1 => "1"})
%{0 => "0", 1 => "1"}

Keys to camelcase:

iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}], keys: :camelcase)
[%{id: 1, myKey: "bar"}, %{value: "a"}]
iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "foo"}, keys: :camelcase)
%{id: 1, myKey: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}
# Keys to snake case:
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}

Convert all structs into maps

iex> map = %{
...> list: [
...> %MyStruct{id: 1, my_key: "foo"}
...> ],
...> struct: %MyStruct{id: 2, my_key: "bar"},
...> other_struct: %MyStruct{id: 3, my_key: %MyStruct{id: 4, my_key: nil}}
...> }
...> NiceMaps.parse(map, convert_structs: true)
%{
list: [
%{id: 1, my_key: "foo"}
],
struct: %{id: 2, my_key: "bar"},
other_struct: %{id: 3, my_key: %{id: 4, my_key: nil}}
}

Convert string keys to existing atom

iex> map = %{
...> "key1" => "value 1",
...> "nested" => %{"key2" => "value 2"},
...> "list" => [%{"key3" => "value 3", "key4" => "value 4"}],
...> 1 => "an integer key",
...> %MyStruct{} => "a struct key"
...> }
iex> [:key1, :key2, :key3, :key4, :nested, :list] # Make sure atoms exist
iex> NiceMaps.parse(map, key_type: :existing_atom)
%{
:key1 => "value 1",
:nested => %{key2: "value 2"},
:list => [%{key3: "value 3", key4: "value 4"}],
1 => "an integer key",
%MyStruct{} => "a struct key"
}

Mix it all together

iex> map = %{
...> "hello_there" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> NiceMaps.parse(map, keys: :camelcase, key_type: :string)
%{"helloThere" => [%{"aA" => "asdf"}, %{"aA" => "bhjk"}, "a string", 1], "thingA" => "thing A", "thingB" => "thing B"}
iex> map = %{
...> "helloThere" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> [:hello_there, :thing_a, :thing_b] # make sure atoms exist
iex> NiceMaps.parse(map, keys: :snake_case, key_type: :existing_atom)
%{:hello_there => [%{:a_a => "asdf"}, %{:a_a => "bhjk"}, "a string", 1], :thing_a => "thing A", :thing_b => "thing B"}

Installation

If available in Hex, the package can be installed by adding nice_maps to your list of dependencies in mix.exs:

defdepsdo[{:nice_maps,"~> 0.1.0"}]end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/nice_maps.

About

Build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Hex Version

NiceMaps

NiceMaps provides a single function parse to convert maps into the desired format.

It can build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

🔥 Danger Zone 🔥

NiceMaps uses String.to_existing_atom/1 for conversions from string keys to atom keys and camelcase-snake_case, so please make sure your atoms exists before attempting something like

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase)# ** (ArgumentError) argument error# :erlang.binary_to_existing_atom("thisDoesNotExistAsCamelcase", :utf8)

If you need to convert a map with unknown atoms, please use string keys instead:

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase,key_type: :string)# %{"thisDoesNotExistAsCamelcase" => "abc"}# or%{"this_does_not_exist_as_camelcase"=>"abc"}|>NiceMaps.parse(keys: :camelcase)

NiceMaps does not provide a key_type: :atom option for the same reason explained later, but you can convert keys to existing atoms using key_type: :existing_atom. If you absolutely insist on creating unknown atoms, there is a way to do it, but I will leave it to you to figure it out from the code (because I think it is a bad idea, and you should really know what you are doing before using it.)

How to use it, and what for

Many people prefer working with atom keys over string keys, because you get some nice syntactic sugar like map.key and the JSON like notation %{key: "value"}, but because atoms are not garbage collected, web frameworks provide parameters as string key maps (otherwise an attacker could flood your memory with atoms until your server crashes.)

So, lets say you have parameters in a Phoenix controller and you want to convert the keys into atoms:

# We only allow these keys, you could call it the "strong parameters approach"@allowed_keys["a","b","c"]defmy_controller(conn,params)doparams|>Map.take(@allowed_keys)|>NiceMaps.parse(key_type: :existing_atom)|>MyContext.create_a_thing()end

Another possible use case is JSON parsing. If you have a map that could or could not have struct values, libraries like Jason will explode on you, and you have to implement the Jason.Encoder protocol for your struct - which is not possible if you do not have control over the structs. NiceMaps to the rescue:

converted=%MyStruct{a: "a",b: "b",a_struct: %MyOtherStruct{c: "c"}}|>NiceMaps.parse(convert_structs: true)# {a: "a", b: "b", a_struct: %{c: "c"}}Jason.encode!(converted)

Last but not least, converting keys from snake case (with_underscore) to camelcase (likeThis) and vice versa. Different protocols/frameworks/programing languages use different conventions, snake case vs camelcase is one of those where there is no right or wrong, but you might want to convert - for example - a graphql response to a more "elixiry" map (using Neuron for this example):

{:ok,%{body: response}}=Neuron.query("""{ aThing { aField }}""")NiceMaps.parse(response,keys: :snake_case)# %{a_thing: %{a_field: "whatever"}}

Options

  • :keys one of :camelcase or :snake_case
  • :convert_structs one of true or false, default: false
  • :key_type, one of :string or :existing_atom

Examples

Without Options:

iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "bar"})
%{id: 1, my_key: "bar"}
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}])
[%{id: 1, my_key: "bar"}, %{value: "a"}]
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, "String"])
[%{id: 1, my_key: "bar"}, "String"]
iex> NiceMaps.parse(%{0 => "0", 1 => "1"})
%{0 => "0", 1 => "1"}

Keys to camelcase:

iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}], keys: :camelcase)
[%{id: 1, myKey: "bar"}, %{value: "a"}]
iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "foo"}, keys: :camelcase)
%{id: 1, myKey: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}
# Keys to snake case:
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}

Convert all structs into maps

iex> map = %{
...> list: [
...> %MyStruct{id: 1, my_key: "foo"}
...> ],
...> struct: %MyStruct{id: 2, my_key: "bar"},
...> other_struct: %MyStruct{id: 3, my_key: %MyStruct{id: 4, my_key: nil}}
...> }
...> NiceMaps.parse(map, convert_structs: true)
%{
list: [
%{id: 1, my_key: "foo"}
],
struct: %{id: 2, my_key: "bar"},
other_struct: %{id: 3, my_key: %{id: 4, my_key: nil}}
}

Convert string keys to existing atom

iex> map = %{
...> "key1" => "value 1",
...> "nested" => %{"key2" => "value 2"},
...> "list" => [%{"key3" => "value 3", "key4" => "value 4"}],
...> 1 => "an integer key",
...> %MyStruct{} => "a struct key"
...> }
iex> [:key1, :key2, :key3, :key4, :nested, :list] # Make sure atoms exist
iex> NiceMaps.parse(map, key_type: :existing_atom)
%{
:key1 => "value 1",
:nested => %{key2: "value 2"},
:list => [%{key3: "value 3", key4: "value 4"}],
1 => "an integer key",
%MyStruct{} => "a struct key"
}

Mix it all together

iex> map = %{
...> "hello_there" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> NiceMaps.parse(map, keys: :camelcase, key_type: :string)
%{"helloThere" => [%{"aA" => "asdf"}, %{"aA" => "bhjk"}, "a string", 1], "thingA" => "thing A", "thingB" => "thing B"}
iex> map = %{
...> "helloThere" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> [:hello_there, :thing_a, :thing_b] # make sure atoms exist
iex> NiceMaps.parse(map, keys: :snake_case, key_type: :existing_atom)
%{:hello_there => [%{:a_a => "asdf"}, %{:a_a => "bhjk"}, "a string", 1], :thing_a => "thing A", :thing_b => "thing B"}

Installation

If available in Hex, the package can be installed by adding nice_maps to your list of dependencies in mix.exs:

defdepsdo[{:nice_maps,"~> 0.1.0"}]end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/nice_maps.

About

Build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Hex Version

NiceMaps

NiceMaps provides a single function parse to convert maps into the desired format.

It can build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

🔥 Danger Zone 🔥

NiceMaps uses String.to_existing_atom/1 for conversions from string keys to atom keys and camelcase-snake_case, so please make sure your atoms exists before attempting something like

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase)# ** (ArgumentError) argument error# :erlang.binary_to_existing_atom("thisDoesNotExistAsCamelcase", :utf8)

If you need to convert a map with unknown atoms, please use string keys instead:

%{this_does_not_exist_as_camelcase: "abc"}|>NiceMaps.parse(keys: :camelcase,key_type: :string)# %{"thisDoesNotExistAsCamelcase" => "abc"}# or%{"this_does_not_exist_as_camelcase"=>"abc"}|>NiceMaps.parse(keys: :camelcase)

NiceMaps does not provide a key_type: :atom option for the same reason explained later, but you can convert keys to existing atoms using key_type: :existing_atom. If you absolutely insist on creating unknown atoms, there is a way to do it, but I will leave it to you to figure it out from the code (because I think it is a bad idea, and you should really know what you are doing before using it.)

How to use it, and what for

Many people prefer working with atom keys over string keys, because you get some nice syntactic sugar like map.key and the JSON like notation %{key: "value"}, but because atoms are not garbage collected, web frameworks provide parameters as string key maps (otherwise an attacker could flood your memory with atoms until your server crashes.)

So, lets say you have parameters in a Phoenix controller and you want to convert the keys into atoms:

# We only allow these keys, you could call it the "strong parameters approach"@allowed_keys["a","b","c"]defmy_controller(conn,params)doparams|>Map.take(@allowed_keys)|>NiceMaps.parse(key_type: :existing_atom)|>MyContext.create_a_thing()end

Another possible use case is JSON parsing. If you have a map that could or could not have struct values, libraries like Jason will explode on you, and you have to implement the Jason.Encoder protocol for your struct - which is not possible if you do not have control over the structs. NiceMaps to the rescue:

converted=%MyStruct{a: "a",b: "b",a_struct: %MyOtherStruct{c: "c"}}|>NiceMaps.parse(convert_structs: true)# {a: "a", b: "b", a_struct: %{c: "c"}}Jason.encode!(converted)

Last but not least, converting keys from snake case (with_underscore) to camelcase (likeThis) and vice versa. Different protocols/frameworks/programing languages use different conventions, snake case vs camelcase is one of those where there is no right or wrong, but you might want to convert - for example - a graphql response to a more "elixiry" map (using Neuron for this example):

{:ok,%{body: response}}=Neuron.query("""{ aThing { aField }}""")NiceMaps.parse(response,keys: :snake_case)# %{a_thing: %{a_field: "whatever"}}

Options

  • :keys one of :camelcase or :snake_case
  • :convert_structs one of true or false, default: false
  • :key_type, one of :string or :existing_atom

Examples

Without Options:

iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "bar"})
%{id: 1, my_key: "bar"}
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}])
[%{id: 1, my_key: "bar"}, %{value: "a"}]
iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, "String"])
[%{id: 1, my_key: "bar"}, "String"]
iex> NiceMaps.parse(%{0 => "0", 1 => "1"})
%{0 => "0", 1 => "1"}

Keys to camelcase:

iex> NiceMaps.parse([%MyStruct{id: 1, my_key: "bar"}, %{value: "a"}], keys: :camelcase)
[%{id: 1, myKey: "bar"}, %{value: "a"}]
iex> NiceMaps.parse(%MyStruct{id: 1, my_key: "foo"}, keys: :camelcase)
%{id: 1, myKey: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}
# Keys to snake case:
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%MyCamelStruct{id: 1, myKey: "foo"}, keys: :snake_case)
%{id: 1, my_key: "foo"}
iex> NiceMaps.parse(%{"string" => "value", "another_string" => "value"}, keys: :camelcase)
%{"string" => "value", "anotherString" => "value"}

Convert all structs into maps

iex> map = %{
...> list: [
...> %MyStruct{id: 1, my_key: "foo"}
...> ],
...> struct: %MyStruct{id: 2, my_key: "bar"},
...> other_struct: %MyStruct{id: 3, my_key: %MyStruct{id: 4, my_key: nil}}
...> }
...> NiceMaps.parse(map, convert_structs: true)
%{
list: [
%{id: 1, my_key: "foo"}
],
struct: %{id: 2, my_key: "bar"},
other_struct: %{id: 3, my_key: %{id: 4, my_key: nil}}
}

Convert string keys to existing atom

iex> map = %{
...> "key1" => "value 1",
...> "nested" => %{"key2" => "value 2"},
...> "list" => [%{"key3" => "value 3", "key4" => "value 4"}],
...> 1 => "an integer key",
...> %MyStruct{} => "a struct key"
...> }
iex> [:key1, :key2, :key3, :key4, :nested, :list] # Make sure atoms exist
iex> NiceMaps.parse(map, key_type: :existing_atom)
%{
:key1 => "value 1",
:nested => %{key2: "value 2"},
:list => [%{key3: "value 3", key4: "value 4"}],
1 => "an integer key",
%MyStruct{} => "a struct key"
}

Mix it all together

iex> map = %{
...> "hello_there" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> NiceMaps.parse(map, keys: :camelcase, key_type: :string)
%{"helloThere" => [%{"aA" => "asdf"}, %{"aA" => "bhjk"}, "a string", 1], "thingA" => "thing A", "thingB" => "thing B"}
iex> map = %{
...> "helloThere" => [%{"aA" => "asdf"}, %{"a_a" => "bhjk"}, "a string", 1],
...> thingA: "thing A",
...> thing_b: "thing B"
...> }
iex> [:hello_there, :thing_a, :thing_b] # make sure atoms exist
iex> NiceMaps.parse(map, keys: :snake_case, key_type: :existing_atom)
%{:hello_there => [%{:a_a => "asdf"}, %{:a_a => "bhjk"}, "a string", 1], :thing_a => "thing A", :thing_b => "thing B"}

Installation

If available in Hex, the package can be installed by adding nice_maps to your list of dependencies in mix.exs:

defdepsdo[{:nice_maps,"~> 0.1.0"}]end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/nice_maps.

About

Build camelcase/snake_case keys, convert string keys to atom keys and vice versa, or convert structs to maps.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages