From b82e04732ab34c0b56caea5f8062aac5c4e21b91 Mon Sep 17 00:00:00 2001
From: Colin DAMON <cdamon@ippon.fr>
Date: Wed, 15 Sep 2021 19:16:30 +0200
Subject: [PATCH] Features order explaination

---
 .gitlab-ci.yml                                |  1 +
 names/.gitlab-ci.yml                          | 11 ++++
 names/pom.xml                                 | 25 +++++++++
 names/readme.md                               |  9 ++++
 .../main/java/fr/craft/kata/Application.java  | 23 +++++++++
 .../application/UsersApplicationService.java  | 18 +++++++
 .../java/fr/craft/kata/domain/Firstname.java  | 51 +++++++++++++++++++
 .../java/fr/craft/kata/domain/Lastname.java   | 26 ++++++++++
 .../main/java/fr/craft/kata/domain/Name.java  | 25 +++++++++
 .../main/java/fr/craft/kata/domain/Names.java | 27 ++++++++++
 .../fr/craft/kata/domain/UsersRepository.java |  7 +++
 .../infrastructure/primary/JavaUsers.java     | 18 +++++++
 .../secondary/InMemoryUsersRepository.java    | 18 +++++++
 .../craft/kata/domain/FirstnameUnitTest.java  | 35 +++++++++++++
 .../craft/kata/domain/LastnameUnitTest.java   | 29 +++++++++++
 .../fr/craft/kata/domain/NameUnitTest.java    | 30 +++++++++++
 .../fr/craft/kata/domain/NamesFixture.java    | 15 ++++++
 .../fr/craft/kata/domain/NamesUnitTest.java   | 30 +++++++++++
 .../primary/PersonsComponentTest.java         | 27 ++++++++++
 readme.md                                     |  1 +
 20 files changed, 426 insertions(+)
 create mode 100644 names/.gitlab-ci.yml
 create mode 100644 names/pom.xml
 create mode 100644 names/readme.md
 create mode 100644 names/src/main/java/fr/craft/kata/Application.java
 create mode 100644 names/src/main/java/fr/craft/kata/application/UsersApplicationService.java
 create mode 100644 names/src/main/java/fr/craft/kata/domain/Firstname.java
 create mode 100644 names/src/main/java/fr/craft/kata/domain/Lastname.java
 create mode 100644 names/src/main/java/fr/craft/kata/domain/Name.java
 create mode 100644 names/src/main/java/fr/craft/kata/domain/Names.java
 create mode 100644 names/src/main/java/fr/craft/kata/domain/UsersRepository.java
 create mode 100644 names/src/main/java/fr/craft/kata/infrastructure/primary/JavaUsers.java
 create mode 100644 names/src/main/java/fr/craft/kata/infrastructure/secondary/InMemoryUsersRepository.java
 create mode 100644 names/src/test/java/fr/craft/kata/domain/FirstnameUnitTest.java
 create mode 100644 names/src/test/java/fr/craft/kata/domain/LastnameUnitTest.java
 create mode 100644 names/src/test/java/fr/craft/kata/domain/NameUnitTest.java
 create mode 100644 names/src/test/java/fr/craft/kata/domain/NamesFixture.java
 create mode 100644 names/src/test/java/fr/craft/kata/domain/NamesUnitTest.java
 create mode 100644 names/src/test/java/fr/craft/kata/infrastructure/primary/PersonsComponentTest.java

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0b825bb3..dd665353 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 00000000..8344009c
--- /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 00000000..8ef958b7
--- /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 00000000..e446c7df
--- /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 00000000..702dae65
--- /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 00000000..e60bb716
--- /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 00000000..2d2eff9b
--- /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 00000000..d004e3b1
--- /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 00000000..3d08e560
--- /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 00000000..df714678
--- /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 00000000..a78f74f2
--- /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 00000000..8f83b7b0
--- /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 00000000..476a8e3c
--- /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 00000000..9441b7c3
--- /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 00000000..4b13c797
--- /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 00000000..be854ea3
--- /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 00000000..334515f8
--- /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 00000000..9640a2f1
--- /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 00000000..325f447a
--- /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 26510f98..fbf83144 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é
 
-- 
GitLab