Skip to content

Commit a94eb4e

Browse files
authored
🏷️ Define the Gitmoji data model (#8)
1 parent 98d0073 commit a94eb4e

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed

src/gitmojis/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .model import Gitmoji, Guide
2+
3+
__all__ = [
4+
"Gitmoji",
5+
"Guide",
6+
]

src/gitmojis/model.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from collections import UserList
2+
from dataclasses import dataclass
3+
from typing import Iterable, Literal
4+
5+
6+
@dataclass(frozen=True, kw_only=True)
7+
class Gitmoji:
8+
"""Represents a single Gitmoji and its data.
9+
10+
The data model is adapted from the schema of the original Gitmoji project.
11+
12+
Attributes:
13+
emoji: The emoji symbol representing the Gitmoji.
14+
entity: The HTML entity corresponding to the Gitmoji.
15+
code: The Markdown code of the Gitmoji's emoji.
16+
description: A brief description of changes introduced by commits and pull
17+
requests marked by the Gitmoji.
18+
name: The user-defined name or identifier of the Gitmoji.
19+
semver: The Semantic Versioning level affected by the commits or pull requests
20+
marked by the emoji associated with the Gitmoji, if specified. May be `None`
21+
or one of the following: `"major"`, `"minor"`, `"patch"`.
22+
"""
23+
24+
emoji: str
25+
entity: str
26+
code: str
27+
description: str
28+
name: str
29+
semver: Literal["major", "minor", "patch"] | None
30+
31+
32+
class Guide(UserList[Gitmoji]):
33+
"""Represents a list of (a "guide" through) `Gitmoji` objects.
34+
35+
This class is used to create a collection of `Gitmoji` objects, providing a simple
36+
framework for accessing various Gitmojis and their data.
37+
"""
38+
39+
def __init__(self, *, gitmojis: Iterable[Gitmoji] | None = None) -> None:
40+
"""Construct a new `Guide` object.
41+
42+
Args:
43+
gitmojis: An optional iterable of `Gitmoji` objects used to create
44+
the guide. If `None`, the guide is initialized as empty. Note
45+
that the data must be passed as a keyword argument, in contrast
46+
to the implementation provided by the base class.
47+
"""
48+
super().__init__(gitmojis)
49+
50+
@property
51+
def gitmojis(self) -> list[Gitmoji]:
52+
"""Return the guide's data with a semantically meaningful attribute name."""
53+
return self.data

tests/test_gitmoji.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from dataclasses import FrozenInstanceError, fields
2+
3+
import pytest
4+
5+
from gitmojis.model import Gitmoji
6+
7+
8+
@pytest.fixture()
9+
def gitmoji_json():
10+
return {
11+
"emoji": "🐛",
12+
"entity": "🐛",
13+
"code": ":bug:",
14+
"description": "Fix a bug.",
15+
"name": "bug",
16+
"semver": "patch",
17+
}
18+
19+
20+
@pytest.fixture()
21+
def gitmoji(gitmoji_json):
22+
return Gitmoji(**gitmoji_json)
23+
24+
25+
def test_gitmoji_init_populates_fields_from_kwargs(gitmoji_json, gitmoji):
26+
for key, value in gitmoji_json.items():
27+
assert getattr(gitmoji, key) == value
28+
29+
30+
@pytest.mark.parametrize(
31+
"field_name",
32+
[field.name for field in fields(Gitmoji)],
33+
)
34+
def test_gitmoji_is_immutable(gitmoji, field_name):
35+
with pytest.raises(FrozenInstanceError):
36+
setattr(gitmoji, field_name, getattr(gitmoji, field_name))

tests/test_guide.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import pytest
2+
3+
from gitmojis.model import Gitmoji, Guide
4+
5+
6+
@pytest.fixture()
7+
def gitmojis_json():
8+
return [
9+
{
10+
"emoji": "💥",
11+
"entity": "💥",
12+
"code": ":boom:",
13+
"description": "Introduce breaking changes.",
14+
"name": "boom",
15+
"semver": "major",
16+
},
17+
{
18+
"emoji": "✨",
19+
"entity": "✨",
20+
"code": ":sparkles:",
21+
"description": "Introduce new features.",
22+
"name": "sparkles",
23+
"semver": "minor",
24+
},
25+
{
26+
"emoji": "🐛",
27+
"entity": "🐛",
28+
"code": ":bug:",
29+
"description": "Fix a bug.",
30+
"name": "bug",
31+
"semver": "patch",
32+
},
33+
{
34+
"emoji": "📝",
35+
"entity": "📝",
36+
"code": ":memo:",
37+
"description": "Add or update documentation.",
38+
"name": "memo",
39+
"semver": None,
40+
},
41+
]
42+
43+
44+
@pytest.fixture()
45+
def guide(gitmojis_json):
46+
return Guide(gitmojis=[Gitmoji(**gitmoji_json) for gitmoji_json in gitmojis_json])
47+
48+
49+
def test_guide_init_raises_if_called_with_positional_argument():
50+
with pytest.raises(TypeError):
51+
Guide([])
52+
53+
54+
def test_guide_init_succeeds_if_called_with_keyword_argument():
55+
try:
56+
Guide(gitmojis=[])
57+
except TypeError:
58+
pytest.fail()
59+
60+
61+
def test_guide_gitmojis_returns_data(guide):
62+
assert guide.gitmojis == guide.data

0 commit comments

Comments
 (0)