diff --git a/src/protean/domain/__init__.py b/src/protean/domain/__init__.py index 50a62a5d..7f8644e2 100644 --- a/src/protean/domain/__init__.py +++ b/src/protean/domain/__init__.py @@ -11,7 +11,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple, Union from uuid import uuid4 -from inflection import parameterize, transliterate, underscore +from inflection import parameterize, titleize, transliterate, underscore from protean.adapters import Brokers, Caches, EmailProviders, Providers from protean.adapters.event_store import EventStore @@ -189,6 +189,23 @@ def __init__( # FIXME Should all protean elements be subclassed from a base element? self._pending_class_resolutions: dict[str, Any] = defaultdict(list) + @property + @lru_cache() + def camel_case_name(self) -> str: + """Return the CamelCase name of the domain. + + The CamelCase name is the name of the domain with the first letter capitalized. + Examples: + - `my_domain` -> `MyDomain` + - `my_domain_1` -> `MyDomain1` + - `my_domain_1_0` -> `MyDomain10` + """ + # Transliterating the name to remove any special characters and camelize + formatted_string = titleize(transliterate(self.name).replace("-", " ")) + + # Eliminate non-alphanumeric characters + return "".join(filter(str.isalnum, formatted_string)) + @property @lru_cache() def normalized_name(self) -> str: diff --git a/tests/domain/tests.py b/tests/domain/tests.py index 8285b337..149ae164 100644 --- a/tests/domain/tests.py +++ b/tests/domain/tests.py @@ -26,7 +26,7 @@ def test_domain_name_string(): def test_normalized_domain_name(): - # A few test cases to check if domain names are normalized correctly + # Test cases to check if domain names are normalized correctly # Each item is a tuple of (name, normalized_name) data = [ ("Foo", "foo"), @@ -50,6 +50,31 @@ def test_normalized_domain_name(): assert domain.normalized_name == result, f"Failed for {name}" +def test_camel_case_domain_name(): + # Test cases to check if domain names are converted to camel case correctly + # Each item is a tuple of (name, camel_case_name) + data = [ + ("My Domain", "MyDomain"), + ("my_domain", "MyDomain"), + ("my-domain", "MyDomain"), + ("foo", "Foo"), + ("foo_bar", "FooBar"), + ("foo_bar_baz", "FooBarBaz"), + ("foo-bar-baz", "FooBarBaz"), + ("foo-bar", "FooBar"), + ("foo_bar_baz", "FooBarBaz"), + ("foo-bar-baz", "FooBarBaz"), + ("donald_e_knuth", "DonaldEKnuth"), + ("donald-e-knuth", "DonaldEKnuth"), + ("Donald E. Knuth", "DonaldEKnuth"), + ("My Domain 1", "MyDomain1"), + ("My Domain 1.0", "MyDomain10"), + ] + for name, result in data: + domain = Domain(__file__, name, load_toml=False) + assert domain.camel_case_name == result, f"Failed for {name}" + + class TestElementRegistration: def test_that_only_recognized_element_types_can_be_registered(self, test_domain): with pytest.raises(NotSupportedError) as exc: