Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand fields to simplify string functions #1

Merged
merged 5 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/jsonapi/plugs/query_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ defmodule JSONAPI.QueryParser do
if JString.field_transformation() == :underscore do
fields
else
JString.underscore(fields)
JString.expand_fields(fields, &JString.underscore/1)
end
end
end
4 changes: 2 additions & 2 deletions lib/jsonapi/plugs/underscore_parameters.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ defmodule JSONAPI.UnderscoreParameters do

import Plug.Conn

import JSONAPI.Utils.String, only: [underscore: 1]
alias JSONAPI.Utils.String, as: JString

@doc false
def init(_opts) do
Expand All @@ -44,7 +44,7 @@ defmodule JSONAPI.UnderscoreParameters do
content_type = get_req_header(conn, "content-type")

if JSONAPI.mime_type() in content_type do
new_params = underscore(params)
new_params = JString.expand_fields(params, &JString.underscore/1)

Map.put(conn, :params, new_params)
else
Expand Down
4 changes: 2 additions & 2 deletions lib/jsonapi/serializer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ defmodule JSONAPI.Serializer do

defp transform_fields(fields) do
case JString.field_transformation() do
:camelize -> JString.camelize(fields)
:dasherize -> JString.dasherize(fields)
:camelize -> JString.expand_fields(fields, &JString.camelize/1)
:dasherize -> JString.expand_fields(fields, &JString.dasherize/1)
_ -> fields
end
end
Expand Down
131 changes: 70 additions & 61 deletions lib/jsonapi/utils/string.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,56 +17,26 @@ defmodule JSONAPI.Utils.String do
iex> underscore("top-posts")
"top_posts"

iex> underscore(:top_posts)
"top_posts"

iex> underscore("-top-posts")
"-top_posts"

iex> underscore("-top--posts-")
"-top--posts-"

iex> underscore(%{"foo-bar" => "baz"})
%{"foo_bar" => "baz"}

iex> underscore({"foo-bar", "dollar-sol"})
{"foo_bar", "dollar-sol"}

iex> underscore({"foo-bar", %{"a-d" => "z-8"}})
{"foo_bar", %{"a_d" => "z-8"}}

iex> underscore(%{"f-b" => %{"a-d" => "z"}, "c-d" => "e"})
%{"f_b" => %{"a_d" => "z"}, "c_d" => "e"}

iex> underscore(:"foo-bar")
:foo_bar

iex> underscore(%{"f-b" => "a-d"})
%{"f_b" => "a-d"}

"""
@spec underscore(String.t()) :: String.t()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

def underscore(value) when is_binary(value) do
String.replace(value, ~r/([a-zA-Z0-9])-([a-zA-Z0-9])/, "\\1_\\2")
end

def underscore(map) when is_map(map) do
Enum.into(map, %{}, &underscore/1)
end

def underscore({key, value}) when is_map(value) do
{underscore(key), underscore(value)}
end

def underscore({key, value}) do
{underscore(key), value}
end

@spec underscore(atom) :: atom
def underscore(value) when is_atom(value) do
value
|> to_string()
|> underscore()
|> String.to_atom()
end

def underscore(value) do
value
end

@doc """
Expand All @@ -86,32 +56,18 @@ defmodule JSONAPI.Utils.String do
"_top__posts_"

"""
@spec dasherize(atom) :: atom
def dasherize(value) when is_atom(value) do
value
|> to_string()
|> dasherize()
end

@spec dasherize(String.t()) :: String.t()
def dasherize(value) when is_binary(value) do
String.replace(value, ~r/([a-zA-Z0-9])_([a-zA-Z0-9])/, "\\1-\\2")
end

def dasherize(%{__struct__: _} = value) when is_map(value) do
value
end

def dasherize(value) when is_map(value) do
Enum.into(value, %{}, &dasherize/1)
end

def dasherize({key, value}) do
if is_map(value) do
{dasherize(key), dasherize(value)}
else
{dasherize(key), value}
end
end

@doc """
Replace underscores or dashes between words in `value` with camelCasing

Expand All @@ -122,19 +78,24 @@ defmodule JSONAPI.Utils.String do
iex> camelize("top_posts")
"topPosts"

iex> camelize(:top_posts)
"topPosts"

iex> camelize("_top_posts")
"_topPosts"

iex> camelize("_top__posts_")
"_top__posts_"

"""
@spec camelize(atom) :: atom
def camelize(value) when is_atom(value) do
value
|> to_string()
|> camelize()
end

@spec camelize(String.t()) :: String.t()
def camelize(value) when is_binary(value) do
with words <-
Regex.split(
Expand All @@ -154,20 +115,68 @@ defmodule JSONAPI.Utils.String do
[String.capitalize(h)] ++ camelize_list(t)
end

def camelize(%{__struct__: _} = value) when is_map(value) do
value
@doc """

## Examples

iex> expand_fields(%{"foo-bar" => "baz"}, &underscore/1)
%{"foo_bar" => "baz"}

iex> expand_fields(%{"foo_bar" => "baz"}, &dasherize/1)
%{"foo-bar" => "baz"}

iex> expand_fields(%{"foo-bar" => "baz"}, &camelize/1)
%{"fooBar" => "baz"}

iex> expand_fields({"foo-bar", "dollar-sol"}, &underscore/1)
{"foo_bar", "dollar-sol"}

iex> expand_fields({"foo-bar", %{"a-d" => "z-8"}}, &underscore/1)
{"foo_bar", %{"a_d" => "z-8"}}

iex> expand_fields(%{"f-b" => %{"a-d" => "z"}, "c-d" => "e"}, &underscore/1)
%{"f_b" => %{"a_d" => "z"}, "c_d" => "e"}

iex> expand_fields(%{"f-b" => %{"a-d" => %{"z-w" => "z"}}, "c-d" => "e"}, &underscore/1)
%{"f_b" => %{"a_d" => %{"z_w" => "z"}}, "c_d" => "e"}

iex> expand_fields(:"foo-bar", &underscore/1)
"foo_bar"

iex> expand_fields(:foo_bar, &dasherize/1)
"foo-bar"

iex> expand_fields(:"foo-bar", &camelize/1)
"fooBar"

iex> expand_fields(%{"f-b" => "a-d"}, &underscore/1)
%{"f_b" => "a-d"}

iex> expand_fields(%{"inserted-at" => ~N[2019-01-17 03:27:24.776957]}, &underscore/1)
%{"inserted_at" => ~N[2019-01-17 03:27:24.776957]}

"""
@spec expand_fields(map, function) :: map
def expand_fields(%{__struct__: _} = value, _fun), do: value

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confess that I'm not really sure what's happening with this __struct__: thing. What's the gist?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pattern matching a naive datetime and other data types - ~N[2019-01-17 03:27:24.776957]


@spec expand_fields(map, function) :: map
def expand_fields(map, fun) when is_map(map) do
Enum.into(map, %{}, &expand_fields(&1, fun))
end

def camelize(value) when is_map(value) do
Enum.into(value, %{}, &camelize/1)
@spec expand_fields(tuple, function) :: tuple
def expand_fields({key, value}, fun) when is_map(value) do
{fun.(key), expand_fields(value, fun)}
end

def camelize({key, value}) do
if is_map(value) do
{camelize(key), camelize(value)}
else
{camelize(key), value}
end
@spec expand_fields(tuple, function) :: tuple
def expand_fields({key, value}, fun) do
{fun.(key), value}
end

@spec expand_fields(any, function) :: any
def expand_fields(value, fun) do
fun.(value)
end

defp normalized_underscore_to_dash_config(value) when is_boolean(value) do
Expand Down