Skip to content
Closed
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 .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[
inputs: ["mix.exs", "{exercises}/**/*.{ex,exs}"]
inputs: ["mix.exs", "{exercises, lib}/**/*.{ex,exs}"]
]
7 changes: 0 additions & 7 deletions exercises/test_helper.exs

This file was deleted.

163 changes: 163 additions & 0 deletions lib/case_helper.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
defmodule ExercismElixir.CaseHelper do
@moduledoc """
Converts strings between lowerCamelCase, UpperCamelCase, snake_case, and kebab-case.
"""

@doc """
Convert lowerCamelCase to UpperCamelCase

## Examples

iex> ExercismElixir.CaseHelper.lower_camel_to_upper_camel_case("fooBarBaz")
"FooBarBaz"
"""
def lower_camel_to_upper_camel_case(str) do
str |> upcase_first_grapheme()
end

@doc """
Convert lowerCamelCase to snake_case

## Examples

iex> ExercismElixir.CaseHelper.lower_camel_to_snake_case("fooBarBaz")
"foo_bar_baz"
"""
def lower_camel_to_snake_case(str) do
str |> String.replace(~r/[[:upper:]]/u, fn char -> "_" <> String.downcase(char) end)
end

@doc """
Convert lowerCamelCase to kebab-case

## Examples

iex> ExercismElixir.CaseHelper.lower_camel_to_kebab_case("fooBarBaz")
"foo-bar-baz"
"""
def lower_camel_to_kebab_case(str) do
str |> String.replace(~r/[[:upper:]]/u, fn char -> "-" <> String.downcase(char) end)
end

@doc """
Convert UpperCamelCase to lowerCamelCase

## Examples

iex> ExercismElixir.CaseHelper.upper_camel_to_lower_camel_case("FooBarBaz")
"fooBarBaz"
"""
def upper_camel_to_lower_camel_case(str) do
str |> downcase_first_grapheme()
end

@doc """
Convert UpperCamelCase to snake_case

## Examples

iex> ExercismElixir.CaseHelper.upper_camel_to_snake_case("FooBarBaz")
"foo_bar_baz"
"""
def upper_camel_to_snake_case(str) do
str |> upper_camel_to_lower_camel_case() |> lower_camel_to_snake_case()
end

@doc """
Convert UpperCamelCase to kebab-case

## Examples

iex> ExercismElixir.CaseHelper.upper_camel_to_kebab_case("FooBarBaz")
"foo-bar-baz"
"""
def upper_camel_to_kebab_case(str) do
str |> upper_camel_to_lower_camel_case() |> lower_camel_to_kebab_case()
end

@doc """
Convert snake_case to lowerCamelCase

## Examples

iex> ExercismElixir.CaseHelper.snake_to_lower_camel_case("foo_bar_baz")
"fooBarBaz"
"""
def snake_to_lower_camel_case(str) do
str |> String.replace(~r/_[[:alpha:]]/u, fn "_" <> char -> String.upcase(char) end)
end

@doc """
Convert snake_case to UpperCamelCase

## Examples

iex> ExercismElixir.CaseHelper.snake_to_upper_camel_case("foo_bar_baz")
"FooBarBaz"
"""
def snake_to_upper_camel_case(str) do
str |> snake_to_lower_camel_case() |> upcase_first_grapheme()
end

@doc """
Convert snake_case to kebab-case

## Examples

iex> ExercismElixir.CaseHelper.snake_to_kebab_case("foo_bar_baz")
"foo-bar-baz"
"""
def snake_to_kebab_case(str) do
str |> String.replace("_", "-")
end

@doc """
Convert kebab-case to lowerCamelCase

## Examples

iex> ExercismElixir.CaseHelper.kebab_to_lower_camel_case("foo-bar-baz")
"fooBarBaz"
"""
def kebab_to_lower_camel_case(str) do
str |> String.replace(~r/-[[:alpha:]]/u, fn "-" <> char -> String.upcase(char) end)
end

@doc """
Convert kebab-case to UpperCamelCase

## Examples

iex> ExercismElixir.CaseHelper.kebab_to_upper_camel_case("foo-bar-baz")
"FooBarBaz"
"""
def kebab_to_upper_camel_case(str) do
str |> kebab_to_lower_camel_case() |> upcase_first_grapheme()
end

@doc """
Convert kebab-case to snake_case

## Examples

iex> ExercismElixir.CaseHelper.kebab_to_snake_case("foo-bar-baz")
"foo_bar_baz"
"""
def kebab_to_snake_case(str) do
str |> String.replace("-", "_")
end

defp downcase_first_grapheme(str) do
case String.next_grapheme(str) do
{first, rest} -> String.downcase(first) <> rest
nil -> ""
end
end

defp upcase_first_grapheme(str) do
case String.next_grapheme(str) do
{first, rest} -> String.upcase(first) <> rest
nil -> ""
end
end
end
38 changes: 38 additions & 0 deletions lib/generator.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
defmodule ExercismElixir.Generator do
alias ExercismElixir.CaseHelper

def canonical_data(exercise_name) do
"../exercism-problem-specifications/exercises/$EXERCISE_NAME/canonical-data.json"
|> String.replace("$EXERCISE_NAME", exercise_name)
|> File.read!()
|> Jason.decode!()
end

def exercise_to_module(name) do
CaseHelper.kebab_to_upper_camel_case(name)
end

def exercise_to_test_module(name) do
exercise_to_module(name) <> "Test"
end

def exercise_to_test_file(name) do
CaseHelper.kebab_to_snake_case(name) <> "_test.exs"
end

def test_file_contents(data) do
module = data |> Map.fetch!("exercise") |> exercise_to_test_module()
version = data |> Map.fetch!("version")

~s"""
defmodule #{module} do
# canonical data version #{version}
use ExUnit.Case
end
"""
end

def write(contents, file_path) do
File.write!(file_path, contents)
end
end
2 changes: 2 additions & 0 deletions lib/generators/hello_world_generator.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
defmodule ExercismElixir.Generator.HelloWorld do
end
11 changes: 6 additions & 5 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
defmodule ExercismTestRunner.Mixfile do
defmodule ExercismElixir.Mixfile do
use Mix.Project

def project do
[
app: :tests,
app: :exercism_elixir,
version: "0.0.1",
elixir: "~> 1.3",
deps: deps(),
test_paths: ["exercises"],
consolidate_protocols: false,
dialyzer: [paths: ["tmp/build"]]
]
end
Expand All @@ -18,6 +16,9 @@ defmodule ExercismTestRunner.Mixfile do
end

defp deps do
[{:dialyxir, "~> 0.5"}]
[
{:jason, "~> 1.1"},
{:dialyxir, "~> 0.5"}
]
end
end
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
%{
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], []},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
}
50 changes: 50 additions & 0 deletions test/case_helper_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
defmodule ExercismElixir.CaseHelperTest do
use ExUnit.Case
alias ExercismElixir.CaseHelper
doctest CaseHelper

test "converts cases properly" do
assert CaseHelper.lower_camel_to_upper_camel_case("fooBarBaz") == "FooBarBaz"
assert CaseHelper.lower_camel_to_snake_case("fooBarBaz") == "foo_bar_baz"
assert CaseHelper.lower_camel_to_kebab_case("fooBarBaz") == "foo-bar-baz"
assert CaseHelper.upper_camel_to_lower_camel_case("FooBarBaz") == "fooBarBaz"
assert CaseHelper.upper_camel_to_snake_case("FooBarBaz") == "foo_bar_baz"
assert CaseHelper.upper_camel_to_kebab_case("FooBarBaz") == "foo-bar-baz"
assert CaseHelper.snake_to_lower_camel_case("foo_bar_baz") == "fooBarBaz"
assert CaseHelper.snake_to_upper_camel_case("foo_bar_baz") == "FooBarBaz"
assert CaseHelper.snake_to_kebab_case("foo_bar_baz") == "foo-bar-baz"
assert CaseHelper.kebab_to_lower_camel_case("foo-bar-baz") == "fooBarBaz"
assert CaseHelper.kebab_to_upper_camel_case("foo-bar-baz") == "FooBarBaz"
assert CaseHelper.kebab_to_snake_case("foo-bar-baz") == "foo_bar_baz"
end

test "handles non-ascii characters" do
assert CaseHelper.lower_camel_to_upper_camel_case("приветМир") == "ПриветМир"
assert CaseHelper.lower_camel_to_snake_case("приветМир") == "привет_мир"
assert CaseHelper.lower_camel_to_kebab_case("приветМир") == "привет-мир"
assert CaseHelper.upper_camel_to_lower_camel_case("ПриветМир") == "приветМир"
assert CaseHelper.upper_camel_to_snake_case("ПриветМир") == "привет_мир"
assert CaseHelper.upper_camel_to_kebab_case("ПриветМир") == "привет-мир"
assert CaseHelper.snake_to_lower_camel_case("привет_мир") == "приветМир"
assert CaseHelper.snake_to_upper_camel_case("привет_мир") == "ПриветМир"
assert CaseHelper.snake_to_kebab_case("привет_мир") == "привет-мир"
assert CaseHelper.kebab_to_lower_camel_case("привет-мир") == "приветМир"
assert CaseHelper.kebab_to_upper_camel_case("привет-мир") == "ПриветМир"
assert CaseHelper.kebab_to_snake_case("привет-мир") == "привет_мир"
end

test "does not error on empty strings" do
assert CaseHelper.lower_camel_to_upper_camel_case("") == ""
assert CaseHelper.lower_camel_to_snake_case("") == ""
assert CaseHelper.lower_camel_to_kebab_case("") == ""
assert CaseHelper.upper_camel_to_lower_camel_case("") == ""
assert CaseHelper.upper_camel_to_snake_case("") == ""
assert CaseHelper.upper_camel_to_kebab_case("") == ""
assert CaseHelper.snake_to_lower_camel_case("") == ""
assert CaseHelper.snake_to_upper_camel_case("") == ""
assert CaseHelper.snake_to_kebab_case("") == ""
assert CaseHelper.kebab_to_lower_camel_case("") == ""
assert CaseHelper.kebab_to_upper_camel_case("") == ""
assert CaseHelper.kebab_to_snake_case("") == ""
end
end
1 change: 1 addition & 0 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ExUnit.start()