Morpheus (morpheus v0.1.2)

Morpheus is an Elixir library for converting between camelCase and snake_case in Phoenix applications.

This module provides key conversion utilities for JSON payloads, allowing your Phoenix application to seamlessly work with camelCase (JavaScript) and snake_case (Elixir) conventions.

Installation

Add to your mix.exs:

def deps do
  [
    {:morpheus, "~> 0.1.2"}
  ]
end

Key Features

  • Converts between camelCase and snake_case for strings and atoms
  • Handles nested data structures (maps and lists)
  • Preserves non-convertible values
  • Properly handles acronyms (API, HTML, iOS)

Quick Start

# Basic conversion
"user_name" |> Morpheus.snake_to_camel()     # => "userName"
"userName" |> Morpheus.camel_to_snake()      # => "user_name"

# Convert map keys
data = %{user_profile: %{first_name: "John"}}
Morpheus.convert_map_keys(data, &Morpheus.snake_to_camel/1)
# => %{userProfile: %{firstName: "John"}}

Usage with Phoenix

For automatic JSON conversion, see Morpheus.Encoder.

Summary

Functions

Converts a camelCase string or atom to snake_case.

Recursively converts keys in maps and lists using the provided conversion function.

Converts a snake_case string or atom to camelCase.

Functions

Link to this function

camel_to_snake(string)

Converts a camelCase string or atom to snake_case.

String Conversion

# Basic conversion
iex> Morpheus.camel_to_snake("userName")
"user_name"

# Multiple words
iex> Morpheus.camel_to_snake("userFirstName")
"user_first_name"

Acronym Handling

# Consecutive capitals
iex> Morpheus.camel_to_snake("API")
"api"

# Mixed case acronyms
iex> Morpheus.camel_to_snake("APIResponse")
"api_response"

Atom Conversion

iex> Morpheus.camel_to_snake(:userFirstName)
:user_first_name

Other Types

Returns the input unchanged if it's neither a string nor an atom:

iex> Morpheus.camel_to_snake(123)
123

iex> Morpheus.camel_to_snake([1, 2, 3])
[1, 2, 3]
Link to this function

convert_map_keys(struct, conversion_function)

Recursively converts keys in maps and lists using the provided conversion function.

Use Cases

  • Converting API request/response payloads
  • Transforming database results
  • Normalizing data structures

Examples

Nested Maps

iex> data = %{
...>   "userInfo" => %{
...>     "firstName" => "John",
...>     "lastName" => "Doe"
...>   }
...> }
iex> Morpheus.convert_map_keys(data, &Morpheus.camel_to_snake/1)
%{
  "user_info" => %{
    "first_name" => "John",
    "last_name" => "Doe"
  }
}

Lists of Maps

iex> users = [
...>   %{user_id: 1, user_name: "John"},
...>   %{user_id: 2, user_name: "Jane"}
...> ]
iex> Morpheus.convert_map_keys(users, &Morpheus.snake_to_camel/1)
[
  %{userId: 1, userName: "John"},
  %{userId: 2, userName: "Jane"}
]

Mixed Key Types

iex> data = %{"user_name" => "John", last_name: "Doe"}
iex> Morpheus.convert_map_keys(data, &Morpheus.snake_to_camel/1)
%{"userName" => "John", lastName: "Doe"}

Structs

iex> data = %{date: ~D[2025-03-23]}
iex> Morpheus.convert_map_keys(data, &Morpheus.snake_to_camel/1)
%{date: ~D[2025-03-23]}

iex> data = %{file: %Plug.Upload{path: "/tmp/file.txt", content_type: "text/plain", filename: "file.txt"}}
iex> Morpheus.convert_map_keys(data, &Morpheus.snake_to_camel/1)
%{file: %Plug.Upload{path: "/tmp/file.txt", content_type: "text/plain", filename: "file.txt"}}

Non-convertible Values

iex> Morpheus.convert_map_keys("not_a_map", &Morpheus.snake_to_camel/1)
"not_a_map"
Link to this function

snake_to_camel(string)

Converts a snake_case string or atom to camelCase.

String Conversion

# Basic conversion
iex> Morpheus.snake_to_camel("user_name")
"userName"

# Multiple words
iex> Morpheus.snake_to_camel("user_first_name")
"userFirstName"

Special Cases

# Double underscores
iex> Morpheus.snake_to_camel("user__name")
"userName"

# Acronyms
iex> Morpheus.snake_to_camel("api_key")
"apiKey"

# All caps
iex> Morpheus.snake_to_camel("API")
"api"

Atom Conversion

iex> Morpheus.snake_to_camel(:user_first_name)
:userFirstName

Other Types

Returns the input unchanged if it's neither a string nor an atom:

iex> Morpheus.snake_to_camel(123)
123