diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b825bb3b47deef45fcf9c58ef2b9435eb3582dc..dd6653532182e805771d3ac942dc2b4172b18dc8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,3 +41,4 @@ include: - local: "/diamond-ts/.gitlab-ci.yml" - local: "/java-memoizers/.gitlab-ci.yml" - local: "/markov-chain/.gitlab-ci.yml" + - local: "/names/.gitlab-ci.yml" diff --git a/names/.gitlab-ci.yml b/names/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..8344009cb66ec0cd5d4f0fe1f2bb181af7c04aa5 --- /dev/null +++ b/names/.gitlab-ci.yml @@ -0,0 +1,11 @@ +package-names: + variables: + PROJECT_FOLDER: "names" + extends: .java + only: + refs: + - master + - merge_requests + changes: + - ".gitlab-common-ci.yml" + - "names/**/*" diff --git a/names/pom.xml b/names/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..8ef958b707b1b6a68d189fb854c07c6b2568ee7a --- /dev/null +++ b/names/pom.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <version>1.0.0</version> + <groupId>fr.ippon.kata</groupId> + <artifactId>java-parent</artifactId> + <relativePath>../java-parent</relativePath> + </parent> + + <version>1.0.0-SNAPSHOT</version> + <artifactId>names</artifactId> + + <name>Names</name> + + <developers> + <developer> + <email>cdamon@ippon.fr</email> + <name>Colin DAMON</name> + </developer> + </developers> +</project> diff --git a/names/readme.md b/names/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..e446c7dfb86fb2d80155df6f78a7e879a0891600 --- /dev/null +++ b/names/readme.md @@ -0,0 +1,9 @@ +# Dans quel ordre faire les features + +Explications de l'ordre des features dans un projet + +- **Auteurs** : Colin DAMON +- **Date** : 15/09/2021 +- **Langage** : Java +- **Niveau** : Moyen +- **Replay** : [Twitch](https://www.twitch.tv/videos/1149536227) diff --git a/names/src/main/java/fr/craft/kata/Application.java b/names/src/main/java/fr/craft/kata/Application.java new file mode 100644 index 0000000000000000000000000000000000000000..702dae653c168aaabc52697d89f25fad328789a6 --- /dev/null +++ b/names/src/main/java/fr/craft/kata/Application.java @@ -0,0 +1,23 @@ +package fr.craft.kata; + +import fr.craft.kata.application.UsersApplicationService; +import fr.craft.kata.domain.Names; +import fr.craft.kata.infrastructure.primary.JavaUsers; +import fr.craft.kata.infrastructure.secondary.InMemoryUsersRepository; + +public class Application { + + private JavaUsers users; + + public void start() { + UsersApplicationService applicationService = new UsersApplicationService( + new InMemoryUsersRepository()); + + users = new JavaUsers(applicationService); + } + + public Names getManagers() { + return users.managers(); + } + +} diff --git a/names/src/main/java/fr/craft/kata/application/UsersApplicationService.java b/names/src/main/java/fr/craft/kata/application/UsersApplicationService.java new file mode 100644 index 0000000000000000000000000000000000000000..e60bb716d89be0668fdfe501dabc7b78e892765e --- /dev/null +++ b/names/src/main/java/fr/craft/kata/application/UsersApplicationService.java @@ -0,0 +1,18 @@ +package fr.craft.kata.application; + +import fr.craft.kata.domain.Names; +import fr.craft.kata.domain.UsersRepository; + +public class UsersApplicationService { + + private final UsersRepository users; + + public UsersApplicationService(UsersRepository users) { + this.users = users; + } + + public Names managers() { + return users.managers(); + } + +} diff --git a/names/src/main/java/fr/craft/kata/domain/Firstname.java b/names/src/main/java/fr/craft/kata/domain/Firstname.java new file mode 100644 index 0000000000000000000000000000000000000000..2d2eff9b383efaae2a464833b3200a35200c589a --- /dev/null +++ b/names/src/main/java/fr/craft/kata/domain/Firstname.java @@ -0,0 +1,51 @@ +package fr.craft.kata.domain; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public record Firstname(String firstname) + implements Comparable<Firstname> { + private static final Pattern FIRSTNAME_ELEMENT = Pattern + .compile("([a-zA-Z]+)([^a-zA-Z]*)"); + + public Firstname(String firstname) { + assertFirstname(firstname); + + this.firstname = buildFirstname(firstname); + } + + private String buildFirstname(String firstname) { + Matcher matcher = FIRSTNAME_ELEMENT.matcher(firstname); + + StringBuilder result = new StringBuilder(); + while (matcher.find()) { + String name = matcher.group(1); + + result.append(name.substring(0, 1) + .toUpperCase()); + + result.append(name.substring(1, name.length()) + .toLowerCase()); + + result.append(matcher.group(2)); + } + + return result.toString(); + } + + private void assertFirstname(String firstname) { + if (firstname == null || firstname.isBlank()) { + throw new IllegalArgumentException( + "You must set a firstname"); + } + } + + public String get() { + return firstname(); + } + + @Override + public int compareTo(Firstname other) { + return firstname().compareTo(other.firstname()); + } +} diff --git a/names/src/main/java/fr/craft/kata/domain/Lastname.java b/names/src/main/java/fr/craft/kata/domain/Lastname.java new file mode 100644 index 0000000000000000000000000000000000000000..d004e3b1106c38e2132ac9c6d9b3b1e17580b5f6 --- /dev/null +++ b/names/src/main/java/fr/craft/kata/domain/Lastname.java @@ -0,0 +1,26 @@ +package fr.craft.kata.domain; + +public record Lastname(String lastname) + implements Comparable<Lastname> { + public Lastname(String lastname) { + assertLastname(lastname); + + this.lastname = lastname.toUpperCase(); + } + + private void assertLastname(String lastname) { + if (lastname == null || lastname.isBlank()) { + throw new IllegalArgumentException( + "You can't have a null lastname"); + } + } + + public String get() { + return lastname(); + } + + @Override + public int compareTo(Lastname other) { + return lastname().compareTo(other.lastname()); + } +} diff --git a/names/src/main/java/fr/craft/kata/domain/Name.java b/names/src/main/java/fr/craft/kata/domain/Name.java new file mode 100644 index 0000000000000000000000000000000000000000..3d08e560527eb7cb4f6ae94a275d7f32aca45eec --- /dev/null +++ b/names/src/main/java/fr/craft/kata/domain/Name.java @@ -0,0 +1,25 @@ +package fr.craft.kata.domain; + +import java.util.Comparator; + +public record Name(Firstname firstname, Lastname lastname) + implements Comparable<Name> { + + private static final Comparator<Name> COMPARATOR = Comparator + .comparing(Name::firstname) + .thenComparing(Name::lastname); + + public Name(String firstname, String lastname) { + this(new Firstname(firstname), new Lastname(lastname)); + } + + public String get() { + return firstname.get() + " " + lastname.get(); + } + + @Override + public int compareTo(Name other) { + return COMPARATOR.compare(this, other); + } + +} diff --git a/names/src/main/java/fr/craft/kata/domain/Names.java b/names/src/main/java/fr/craft/kata/domain/Names.java new file mode 100644 index 0000000000000000000000000000000000000000..df7146787b16b20b3e0491e0bcbdbbe4216d59d7 --- /dev/null +++ b/names/src/main/java/fr/craft/kata/domain/Names.java @@ -0,0 +1,27 @@ +package fr.craft.kata.domain; + +import java.util.List; + +public record Names(List<Name> names) { + + public Names(Name... names) { + this(buildNames(names)); + } + + private static List<Name> buildNames(Name... names) { + if (names == null || names.length == 0) { + throw new IllegalArgumentException( + "Can't build without names"); + } + + return List.of(names) + .stream() + .sorted() + .toList(); + } + + public List<Name> get() { + return names(); + } + +} diff --git a/names/src/main/java/fr/craft/kata/domain/UsersRepository.java b/names/src/main/java/fr/craft/kata/domain/UsersRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..a78f74f268601d740f1bce13b0643cffaf6b8d05 --- /dev/null +++ b/names/src/main/java/fr/craft/kata/domain/UsersRepository.java @@ -0,0 +1,7 @@ +package fr.craft.kata.domain; + +public interface UsersRepository { + + Names managers(); + +} diff --git a/names/src/main/java/fr/craft/kata/infrastructure/primary/JavaUsers.java b/names/src/main/java/fr/craft/kata/infrastructure/primary/JavaUsers.java new file mode 100644 index 0000000000000000000000000000000000000000..8f83b7b0024f648af432aec5e6e311a4e18ae64f --- /dev/null +++ b/names/src/main/java/fr/craft/kata/infrastructure/primary/JavaUsers.java @@ -0,0 +1,18 @@ +package fr.craft.kata.infrastructure.primary; + +import fr.craft.kata.application.UsersApplicationService; +import fr.craft.kata.domain.Names; + +public class JavaUsers { + + private final UsersApplicationService users; + + public JavaUsers(UsersApplicationService users) { + this.users = users; + } + + public Names managers() { + return users.managers(); + } + +} diff --git a/names/src/main/java/fr/craft/kata/infrastructure/secondary/InMemoryUsersRepository.java b/names/src/main/java/fr/craft/kata/infrastructure/secondary/InMemoryUsersRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..476a8e3cec32ae3614e983e2321ca492a05cbddb --- /dev/null +++ b/names/src/main/java/fr/craft/kata/infrastructure/secondary/InMemoryUsersRepository.java @@ -0,0 +1,18 @@ +package fr.craft.kata.infrastructure.secondary; + +import java.util.List; + +import fr.craft.kata.domain.Name; +import fr.craft.kata.domain.Names; +import fr.craft.kata.domain.UsersRepository; + +public class InMemoryUsersRepository + implements UsersRepository { + + @Override + public Names managers() { + return new Names( + List.of(new Name("jean-paul", "dupond"))); + } + +} diff --git a/names/src/test/java/fr/craft/kata/domain/FirstnameUnitTest.java b/names/src/test/java/fr/craft/kata/domain/FirstnameUnitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9441b7c31fbed1fe34a646d4c7c3d37ac48ac424 --- /dev/null +++ b/names/src/test/java/fr/craft/kata/domain/FirstnameUnitTest.java @@ -0,0 +1,35 @@ +package fr.craft.kata.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class FirstnameUnitTest { + + @Test + void shouldNotBuildWihtoutFirstname() { + assertThatThrownBy(() -> new Firstname(null)) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("firstname"); + } + + @Test + void shouldNotBuildWihtBlankFirstname() { + assertThatThrownBy(() -> new Firstname(" ")) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("firstname"); + } + + @Test + void shouldCapitalizeSimpleFirstname() { + assertThat(new Firstname("jean").get()) + .isEqualTo("Jean"); + } + + @Test + void shouldCapitalizeComposeFirstname() { + assertThat(new Firstname("jean PAUL-jacque").get()) + .isEqualTo("Jean Paul-Jacque"); + } + +} diff --git a/names/src/test/java/fr/craft/kata/domain/LastnameUnitTest.java b/names/src/test/java/fr/craft/kata/domain/LastnameUnitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4b13c79743bb68175b7866d906e05a935fa8ab22 --- /dev/null +++ b/names/src/test/java/fr/craft/kata/domain/LastnameUnitTest.java @@ -0,0 +1,29 @@ +package fr.craft.kata.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class LastnameUnitTest { + + @Test + void shouldNotBuildWithoutLastname() { + assertThatThrownBy(() -> new Lastname(null)) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("lastname"); + } + + @Test + void shouldNotBuildWithBlankLastname() { + assertThatThrownBy(() -> new Lastname(" ")) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("lastname"); + } + + @Test + void shouldGetUpperCaseLastname() { + assertThat(new Lastname("Dupond").get()) + .isEqualTo("DUPOND"); + } + +} diff --git a/names/src/test/java/fr/craft/kata/domain/NameUnitTest.java b/names/src/test/java/fr/craft/kata/domain/NameUnitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..be854ea387fb99ff9d157d83386bb47b5117769e --- /dev/null +++ b/names/src/test/java/fr/craft/kata/domain/NameUnitTest.java @@ -0,0 +1,30 @@ +package fr.craft.kata.domain; + +import static fr.craft.kata.domain.NamesFixture.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +class NameUnitTest { + + @Test + void shouldGetFullName() { + Name name = jeanPaul(); + + assertThat(name.get()).isEqualTo("Jean-Paul DUPOND"); + } + + @Test + void shouldSortNames() { + Name colin = colin(); + List<Name> names = List.of(jeanPaul(), colin) + .stream() + .sorted() + .toList(); + + assertThat(names).containsExactly(colin, jeanPaul()); + } + +} diff --git a/names/src/test/java/fr/craft/kata/domain/NamesFixture.java b/names/src/test/java/fr/craft/kata/domain/NamesFixture.java new file mode 100644 index 0000000000000000000000000000000000000000..334515f80da0a99cbed47d603c18c0306b920aaa --- /dev/null +++ b/names/src/test/java/fr/craft/kata/domain/NamesFixture.java @@ -0,0 +1,15 @@ +package fr.craft.kata.domain; + +public final class NamesFixture { + private NamesFixture() { + } + + public static Name jeanPaul() { + return new Name("Jean-paul", "Dupond"); + } + + public static Name colin() { + return new Name("colin", "damon"); + } + +} diff --git a/names/src/test/java/fr/craft/kata/domain/NamesUnitTest.java b/names/src/test/java/fr/craft/kata/domain/NamesUnitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9640a2f194befe0425ed986c6a9ce16331b8b0cf --- /dev/null +++ b/names/src/test/java/fr/craft/kata/domain/NamesUnitTest.java @@ -0,0 +1,30 @@ +package fr.craft.kata.domain; + +import static fr.craft.kata.domain.NamesFixture.*; +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class NamesUnitTest { + + @Test + void shouldNotBuildWithNullName() { + assertThatThrownBy(() -> new Names((Name[]) null)) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("names"); + } + + @Test + void shouldNotBuildWithoutName() { + assertThatThrownBy(() -> new Names()) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("names"); + } + + @Test + void shouldGetSortedNames() { + assertThat(new Names(jeanPaul(), colin()).get()) + .containsExactly(colin(), jeanPaul()); + } + +} diff --git a/names/src/test/java/fr/craft/kata/infrastructure/primary/PersonsComponentTest.java b/names/src/test/java/fr/craft/kata/infrastructure/primary/PersonsComponentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..325f447a723c2758b156e1f8b8315b9642580078 --- /dev/null +++ b/names/src/test/java/fr/craft/kata/infrastructure/primary/PersonsComponentTest.java @@ -0,0 +1,27 @@ +package fr.craft.kata.infrastructure.primary; + +import static fr.craft.kata.domain.NamesFixture.*; +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import fr.craft.kata.Application; +import fr.craft.kata.domain.Names; + +public class PersonsComponentTest { + + private static final Application application = new Application(); + + @BeforeAll + static void startApplication() { + application.start(); + } + + @Test + void shouldGetUsers() { + assertThat(application.getManagers()) + .isEqualTo(new Names(jeanPaul())); + } + +} diff --git a/readme.md b/readme.md index 26510f9806ec1c9bf6927e150a0c9051ba9b2962..fbf8314451f27fde268d227593d1ec7b45a6e4df 100644 --- a/readme.md +++ b/readme.md @@ -45,6 +45,7 @@ Un kata de code est un petit exercice pensé pour s'entrainer jusqu'à maitriser - [Puzzles](java-puzzles) - [Diamond](java-diamond) - [Markov chain](markov-chain) +- [Names](names) ### Énervé