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

Backported Case2. Left 5 tests failing. #46

Merged
merged 3 commits into from
Feb 14, 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
21 changes: 11 additions & 10 deletions lib/recase.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ defmodule Recase do
iex> Recase.to_pascal("some value")
"SomeValue"
"""
@spec to_pascal(String.t) :: String.t
@spec to_pascal(String.t()) :: String.t()
def to_pascal(value), do: PascalCase.convert(value)

@doc """
Expand All @@ -42,7 +42,7 @@ defmodule Recase do
iex> Recase.to_camel("Some Value")
"someValue"
"""
@spec to_camel(String.t) :: String.t
@spec to_camel(String.t()) :: String.t()
def to_camel(value), do: CamelCase.convert(value)

@doc """
Expand All @@ -56,8 +56,9 @@ defmodule Recase do
iex> Recase.to_snake("someValue")
"some_value"
"""
@spec to_snake(String.t) :: String.t
@spec to_snake(String.t()) :: String.t()
def to_snake(value), do: SnakeCase.convert(value)
defdelegate underscore(value), to: Recase, as: :to_snake

@doc """
Converts string to kebab-case.
Expand All @@ -70,7 +71,7 @@ defmodule Recase do
iex> Recase.to_kebab("some value")
"some-value"
"""
@spec to_kebab(String.t) :: String.t
@spec to_kebab(String.t()) :: String.t()
def to_kebab(value), do: KebabCase.convert(value)

@doc """
Expand All @@ -84,7 +85,7 @@ defmodule Recase do
iex> Recase.to_constant("some value")
"SOME_VALUE"
"""
@spec to_constant(String.t) :: String.t
@spec to_constant(String.t()) :: String.t()
def to_constant(value), do: ConstantCase.convert(value)

@doc ~S"""
Expand All @@ -98,9 +99,9 @@ defmodule Recase do
iex> Recase.to_path("some value", "\\")
"some\\value"
"""
@spec to_path(String.t, String.t) :: String.t
@spec to_path(String.t(), String.t()) :: String.t()
def to_path(value, separator), do: PathCase.convert(value, separator)
@spec to_path(String.t) :: String.t
@spec to_path(String.t()) :: String.t()
def to_path(value), do: PathCase.convert(value)

@doc """
Expand All @@ -114,7 +115,7 @@ defmodule Recase do
iex> Recase.to_dot("some value")
"some.value"
"""
@spec to_dot(String.t) :: String.t
@spec to_dot(String.t()) :: String.t()
def to_dot(value), do: DotCase.convert(value)

@doc """
Expand All @@ -128,7 +129,7 @@ defmodule Recase do
iex> Recase.to_sentence("some value")
"Some value"
"""
@spec to_sentence(String.t) :: String.t
@spec to_sentence(String.t()) :: String.t()
def to_sentence(value), do: SentenceCase.convert(value)

@doc """
Expand All @@ -142,6 +143,6 @@ defmodule Recase do
iex> Recase.to_title("some value")
"Some Value"
"""
@spec to_title(String.t) :: String.t
@spec to_title(String.t()) :: String.t()
def to_title(value), do: TitleCase.convert(value)
end
20 changes: 11 additions & 9 deletions lib/recase/cases/camel_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@ defmodule Recase.CamelCase do

This module should not be used directly.

## Examples

iex> Recase.to_camel "foo_barBaz-λambdaΛambda-привет-Мир"
"fooBarBazΛambdaΛambdaПриветМир"

Read about `camelCase` here:
https://en.wikipedia.org/wiki/Camel_case
"""

import Recase.Replace
import Recase.Generic, only: [rejoin: 2]

@spec convert(String.t) :: String.t
@spec convert(String.t()) :: String.t()
def convert(""), do: ""
def convert(value) do
value
|> String.trim
|> replace(~r/^[_\.\-\s]+/, "")
|> replace(~r/([a-zA-Z]+)([A-Z][a-z\d]+)/, "\\1-\\2")
|> String.downcase
|> replace(~r/[_\.\-\s]+(\w|$)/, fn(_, x) -> String.upcase(x) end)

def convert(value) when is_binary(value) do
with <<char::utf8, rest::binary>> <- rejoin(value, separator: "", case: :title),
do: String.downcase(<<char::utf8>>) <> rest
end
end
16 changes: 9 additions & 7 deletions lib/recase/cases/constant_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ defmodule Recase.ConstantCase do

This module should not be used directly.

## Examples

iex> Recase.to_constant "foo_barBaz-λambdaΛambda-привет-Мир"
"FOO_BAR_BAZ_ΛAMBDA_ΛAMBDA_ПРИВЕТ_МИР"

Constant case is the same as `snake_case`,
but uppercased.
"""

alias Recase.SnakeCase
import Recase.Generic, only: [rejoin: 2]

@spec convert(String.t) :: String.t
def convert(value) do
value
|> SnakeCase.convert
|> String.upcase
end
@spec convert(String.t()) :: String.t()
def convert(value) when is_binary(value),
do: rejoin(value, separator: "_", case: :up)
end
16 changes: 9 additions & 7 deletions lib/recase/cases/dot_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ defmodule Recase.DotCase do

This module should not be used directly.

## Examples

iex> Recase.to_dot "foo_barBaz-λambdaΛambda-привет-Мир"
"foo.bar.baz.λambda.λambda.привет.мир"

`DotCase` is the same as `KebabCase` and `SnakeCase`.
But uses `.` as a separator.
"""

alias Recase.SnakeCase
import Recase.Generic, only: [rejoin: 2]

@sep "."

@spec convert(String.t) :: String.t
def convert(value) do
value
|> SnakeCase.convert
|> String.replace("_", @sep)
end
@spec convert(String.t()) :: String.t()
def convert(value) when is_binary(value),
do: rejoin(value, separator: @sep, case: :down)
end
129 changes: 129 additions & 0 deletions lib/recase/cases/generic.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
defmodule Recase.Generic do
@moduledoc """
Generic module to split and join strings back.

This module should not be used directly.
"""

@splitters Application.get_env(:recase, :delimiters, [?\s, ?\n, ?\t, ?_, ?., ?-, ?#, ??, ?!])

@delimiters (case @splitters do
list when is_list(list) ->
list

:symbol ->
[all, digits, down, up] =
Enum.map([32..127, ?0..?9, ?a..?z, ?A..?Z], &Enum.to_list/1)

all -- (digits ++ down ++ up)
end)

@doc """
Splits the input into **`list`**. Utility function.

## Examples

iex> Recase.Generic.split "foo_barBaz-λambdaΛambda-привет-Мир"
["foo", "bar", "Baz", "λambda", "Λambda", "привет", "Мир"]
"""
@spec split(input :: String.t()) :: [String.t()]
def split(input) when is_binary(input), do: do_split(input)

@doc """
Splits the input and **`rejoins`** it with a separator given. Optionally
converts parts to `downcase`, `upcase` or `titlecase`.

- `opts[:case] :: [:down | :up | :title | :none]`
- `opts[:separator] :: binary() | integer()`

Default separator is `?_`, default conversion is `:downcase` so that
it behaves the same way as `to_snake/1`.

## Examples

iex> Recase.Generic.rejoin "foo_barBaz-λambdaΛambda-привет-Мир", separator: "__"
"foo__bar__baz__λambda__λambda__привет__мир"
"""
@spec rejoin(input :: String.t(), opts :: Keyword.t()) :: String.t()
def rejoin(input, opts \\ []) when is_binary(input) do
mapper =
case Keyword.get(opts, :case, :down) do
:down ->
&String.downcase/1

:title ->
fn <<char::utf8, rest::binary>> ->
String.upcase(<<char::utf8>>) <> String.downcase(rest)
end

:up ->
&String.upcase/1

_ ->
& &1
end

input
|> do_split()
|> Enum.map(mapper)
|> Enum.join(Keyword.get(opts, :separator, ?_))
end

##############################################################################

@spec do_split(input :: String.t(), acc :: [String.t()]) :: [String.t()]
defp do_split(string, acc \\ {"", []})

defp do_split("", {"", acc}), do: Enum.reverse(acc)

defp do_split("", {curr, acc}),
do: do_split("", {"", [curr | acc]})

Enum.each(@delimiters, fn delim ->
defp do_split(<<unquote(delim)::utf8, rest::binary>>, {"", acc}),
do: do_split(rest, {"", acc})

defp do_split(<<unquote(delim), rest::binary>>, {curr, acc}),
do: do_split(rest, {"", [curr | acc]})
end)

Enum.each(?A..?Z, fn char ->
defp do_split(<<unquote(char), rest::binary>>, {"", acc}),
do: do_split(rest, {<<unquote(char)::utf8>>, acc})

defp do_split(<<unquote(char), rest::binary>>, {curr, acc}) do
<<c::utf8, _::binary>> = String.reverse(curr)

if c in ?A..?Z do
do_split(rest, {curr <> <<unquote(char)::utf8>>, acc})
else
do_split(rest, {<<unquote(char)::utf8>>, [curr | acc]})
end
end
end)

[32..64, 91..127]
|> Enum.map(&Enum.to_list/1)
|> Enum.reduce(&Kernel.++/2)
|> Kernel.--(@delimiters)
|> Enum.each(fn char ->
defp do_split(<<unquote(char)::utf8, rest::binary>>, {"", acc}),
do: do_split(rest, {<<unquote(char)::utf8>>, acc})

defp do_split(<<unquote(char), rest::binary>>, {curr, acc}),
do: do_split(rest, {curr <> <<unquote(char)::utf8>>, acc})
end)

defp do_split(<<char::utf8, rest::binary>>, {"", acc}),
do: do_split(rest, {<<char::utf8>>, acc})

@upcase ~r/(?<!\p{Lu})\p{Lu}/u

defp do_split(<<char::utf8, rest::binary>>, {curr, acc}) do
if Regex.match?(@upcase, <<char::utf8>>) do
do_split(rest, {<<char::utf8>>, [curr | acc]})
else
do_split(rest, {curr <> <<char::utf8>>, acc})
end
end
end
16 changes: 9 additions & 7 deletions lib/recase/cases/kebab_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ defmodule Recase.KebabCase do

This module should not be used directly.

## Examples

iex> Recase.to_kebab "foo_barBaz-λambdaΛambda-привет-Мир"
"foo-bar-baz-λambda-λambda-привет-мир"

Read about `kebab-case` here:
https://en.wikipedia.org/wiki/Kebab_case
"""

alias Recase.SnakeCase
import Recase.Generic, only: [rejoin: 2]

@sep "-"

@spec convert(String.t) :: String.t
def convert(value) do
value
|> SnakeCase.convert
|> String.replace("_", @sep)
end
@spec convert(String.t()) :: String.t()
def convert(value) when is_binary(value),
do: rejoin(value, separator: @sep, case: :down)
end
16 changes: 9 additions & 7 deletions lib/recase/cases/pascal_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ defmodule Recase.PascalCase do

This module should not be used directly.

## Examples

iex> Recase.to_pascal "foo_barBaz-λambdaΛambda-привет-Мир"
"FooBarBazΛambdaΛambdaПриветМир"

Read about `PascalCase` here:
https://en.wikipedia.org/wiki/PascalCase

Expand All @@ -21,12 +26,9 @@ defmodule Recase.PascalCase do
For other details see: https://github.com/sobolevn/recase/issues/2
"""

alias Recase.CamelCase
import Recase.Generic, only: [rejoin: 2]

@spec convert(String.t) :: String.t
def convert(""), do: ""
def convert(value) do
<<char::binary-size(1), rest::binary>> = CamelCase.convert(value)
String.upcase(char) <> rest
end
@spec convert(String.t()) :: String.t()
def convert(value) when is_binary(value),
do: rejoin(value, separator: "", case: :title)
end
23 changes: 13 additions & 10 deletions lib/recase/cases/path_case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ defmodule Recase.PathCase do
but inserts path separator to appropriate places.

By default uses `/` as a path separator.

## Examples

iex> Recase.to_path "foo_barBaz-λambdaΛambda-привет-Мир"
"foo/bar/Baz/λambda/Λambda/привет/Мир"
"""

import Recase.Replace
import Recase.Generic, only: [rejoin: 2]

@sep "/"

@spec convert(String.t) :: String.t
@spec convert(String.t(), String.t()) :: String.t()
def convert(value, separator \\ @sep)
def convert("", _), do: ""
def convert(value, separator) do
value
|> String.trim
|> replace(~r/[\s\.\-_]/, separator)
|> replace(~r/([a-z\d])([A-Z])/, "\\1#{separator}\\2")
|> replace(~r/([A-Z]+)([A-Z][a-z\d]+)/, "\\1#{separator}\\2")
end

def convert(<<separator::binary-size(1), rest::binary>>, separator),
do: separator <> convert(rest, separator)

def convert(value, separator) when is_binary(value),
do: rejoin(value, separator: separator, case: :none)
end
Loading