Add link to Conventional Commits
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Fred Pauchet 2022-06-18 15:16:03 +02:00
parent f6821ff222
commit af8b7c41b0
4 changed files with 78 additions and 50 deletions

View File

@ -38,7 +38,7 @@ L'administration permet de décrire les données qui peuvent être modifiées, e
Si vous vous rappelez de l'application que nous avions créée dans la première partie, les URLs reprenaient déjà la partie suivante:
\begin{minted}{python}
\begin{minted}[tabsize=4]{python}
from django.contrib import admin
from django.urls import path
from gwift.views import wish_details
@ -55,7 +55,7 @@ C'est le seul prérequis pour cette partie.
Chaque application nouvellement créée contient par défaut un fichier \texttt{admin.py}, dans lequel il est possible de déclarer les ensembles de données seront accessibles ou éditables.
Ainsi, si nous partons du modèle basique que nous avions détaillé plus tôt, avec des souhaits et des listes de souhaits:
\begin{minted}{python}
\begin{minted}[tabsize=4]{python}
# gwift/wish/models.py
from django.db import models
@ -73,7 +73,7 @@ Ainsi, si nous partons du modèle basique que nous avions détaillé plus tôt,
Nous pouvons facilement arriver au résultat suivant, en ajoutant
quelques lignes de configuration dans ce fichier \texttt{admin.py}:
\begin{minted}{python}
\begin{minted}[tabsize=4]{python}
from django.contrib import admin
from .models import Item, WishList
@ -130,7 +130,7 @@ Ceci nous permet déjà d'ajouter des éléments (Items), des listes de souhaits
La méthode \texttt{get\_absolute\_url(self)} retourne l'URL à laquelle on peut accéder pour obtenir les détails d'une instance. Par exemple:
\end{enumerate}
\begin{minted}{python}
\begin{minted}[tabsize=4]{python}
def get_absolute_url(self):
return reverse('myapp.views.details', args=[self.id])
\end{minted}
@ -141,7 +141,7 @@ def get_absolute_url(self):
Les attributs \texttt{Meta}:
\end{enumerate}
\begin{minted}{python}
\begin{minted}[tabsize=4]{python}
class Meta:
ordering = ['-field1', 'field2']
verbose_name = 'my class in singular'
@ -227,7 +227,7 @@ Il est cependant possible de configurer des permissions spécifiques pour certai
Cela se joue au niveau du \texttt{ModelAdmin}, en implémentant les méthodes suivantes:
\begin{minted}{python}
\begin{minted}[tabsize=4]{python}
def has_add_permission(self, request):
return True
@ -248,7 +248,7 @@ Les relations 1-n sont implémentées au travers de formsets (que l'on a normale
L'implémentation consiste tout d'abord à définir le comportement du type d'objet référencé (la relation -N), puis à inclure cette définition au niveau du type d'objet référençant (la relation 1-).
\begin{minted}{python}
\begin{minted}[tabsize=4]{python}
class WishInline(TabularInline):
model = Wish
@ -290,7 +290,7 @@ Les paramètres d'entrée sont :
Le queryset correspondant à la sélection.
\end{enumerate}
\begin{minted}{python}
\begin{minted}[tabsize=4]{python}
def double_quantity(self, request, queryset):
for obj in queryset.all():
obj.field += 1
@ -302,7 +302,7 @@ def double_quantity(self, request, queryset):
Et pour informer l'utilisateur de ce qui a été réalisé, on peut aussi
lui passer un petit message:
\begin{minted}{python}
\begin{minted}[tabsize=4]{python}
if rows_updated = 0:
self.message_user(request, "Aucun élément n'a été impacté.")
else:

View File

@ -33,7 +33,10 @@ Des équivalents à ces directives existent au niveau des composants, puis au ni
\textbf{CRP} - Common Reuse Principle.
\end{enumerate}
\includegraphics{images/arch-comp-modules.png}
\begin{figure}[H]
\centering
\scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/arch-comp-modules.png}}
\end{figure}
\section{Modules}
@ -116,25 +119,29 @@ En suivant le principe de responsabilité unique, une bonne pratique consiste à
self.content = content
self.published_at = published_at
class DocumentRenderer:
def render(self, document):
if format_type == "XML":
return """<?xml version = "1.0"?>
<document>
<title>{}</title>
<content>{}</content>
<publication_date>{}</publication_date>
</document>""".format(
self.title,
self.content,
self.published_at.isoformat()
)
class DocumentRenderer:
def render(self, document):
if format_type == "XML":
return """<?xml version = "1.0"?>
<document>
<title>{}</title>
<content>{}</content>
<publication_date>{}</publication_date>
</document>""".format(
self.title,
self.content,
self.published_at.isoformat()
)
if format_type == "Markdown":
import markdown
return markdown.markdown(self.content)
if format_type == "Markdown":
import markdown
return markdown.markdown(self.content)
raise ValueError("Format type '{}' is not known".format(format_type))
raise ValueError(
"Format type '{}' is not known".format(
format_type
)
)
\end{minted}
\caption{Isolation du rendu d'un document par rapport à sa modélisation}
\end{listing}
@ -152,7 +159,7 @@ Au niveau architectural, cet équivalent correspondra aux frontières.
\subsection{Open-Closed}
\begin{quote}
For software systems to be easy to change, they must be designed to allow the behavior to change by adding new code instead of changing existing code.
\textit{For software systems to be easy to change, they must be designed to allow the behavior to change by adding new code instead of changing existing code.}
\end{quote}
L'objectif est de rendre le système facile à étendre, en limitant l'impact qu'une modification puisse avoir.
@ -186,7 +193,7 @@ Ceci évite d'avoir à gérer un ensemble conséquent de conditions dans la mét
Nous passerions ainsi de ceci:
\begin{listing}[H]
\begin{minted}{Python}
\begin{minted}[tabsize=4]{python}
class Customer():
def __init__(self, customer_type: str):
self.customer_type = customer_type
@ -207,7 +214,7 @@ Nous passerions ainsi de ceci:
A ceci:
\begin{listing}[H]
\begin{minted}{Python}
\begin{minted}[tabsize=4]{python}
class Customer():
def get_discount(self) -> int:
return 0
@ -235,7 +242,7 @@ De cette manière, nous simplifions également la maintenance de la méthode \te
Nous pouvons également appliquer ceci à notre exemple sur les rendus de document, où le code suivant:
\begin{listing}[H]
\begin{minted}[tabsize=4]{Python}
\begin{minted}[tabsize=4]{python}
class Document:
def __init__(self, title, content, published_at):
self.title = title
@ -268,7 +275,7 @@ Nous pouvons également appliquer ceci à notre exemple sur les rendus de docume
devient le suivant:
\begin{listing}[H]
\begin{minted}[tabsize=4]{Python}
\begin{minted}[tabsize=4]{python}
class Renderer:
def render(self, document):
raise NotImplementedError
@ -321,7 +328,7 @@ Cela bousillerait le principe de substitution, dans la mesure où une instance d
Petit exemple pratique: si nous définissons une méthode \texttt{make\_some\_noise} et une méthode \texttt{eat} sur une classe \texttt{Duck}, et qu'une réflexion avancée (et sans doute un peu alcoolisée) nous dit que "\emph{Puisqu'un \texttt{Lion} fait aussi du bruit, faisons le hériter de notre classe `Canard`"}, nous allons nous retrouver avec ceci:
\begin{listing}[H]
\begin{minted}[tabsize=4]{Python}
\begin{minted}[tabsize=4]{python}
class Duck:
def make_some_noise(self):
print("Kwak")
@ -346,7 +353,7 @@ Nous vous laissons tester la structure ci-dessus en glissant une antilope dans l
Pour revenir à nos exemples de rendus de documents, nous aurions pu faire hériter notre \texttt{MarkdownRenderer} de la classe \texttt{XmlRenderer}:
\begin{listing}[H]
\begin{minted}[tabsize=4]{Python}
\begin{minted}[tabsize=4]{python}
class XmlRenderer:
def render(self, document)
return """<?xml version = "1.0"?>
@ -371,7 +378,7 @@ Pour revenir à nos exemples de rendus de documents, nous aurions pu faire héri
Si nous décidons à un moment d'ajouter une méthode d'entête au niveau de notre classe de rendu XML, notre rendu en Markdown héritera irrémédiablement de cette même méthode:
\begin{listing}[H]
\begin{minted}[tabsize=4]{Python}
\begin{minted}[tabsize=4]{python}
class XmlRenderer:
def header(self):
return """<?xml version = "1.0"?>"""
@ -402,7 +409,7 @@ Le code ci-dessus ne porte pas à conséquence \footnote{Pas immédiatement, en
En revenant à notre proposition d'implémentation, suite au respect d'Open-Closed, une solution serait de n'implémenter la méthode \texttt{header()} qu'au niveau de la classe \texttt{XmlRenderer}:
\begin{listing}[H]
\begin{minted}[tabsize=4]{Python}
\begin{minted}[tabsize=4]{python}
class Renderer:
def render(self, document):
raise NotImplementedError

View File

@ -69,7 +69,7 @@ La \href{https://fr.wikipedia.org/wiki/Nombre_cyclomatique}{complexité cyclomat
Quand le cycle d'exécution du code rencontre une condition, cette condition peut être évalue à VRAI ou à FAUX.
L'exécution du code dispose donc de deux embranchements, correspondant chacun à un résultat de cette condition.
Le code suivant \autoref{cyclomatic-simple-code} a une complexité cyclomatique 1; il s'agit du cas le plus simple que nous pouvons implémenter : l'exécution du code rentre dans la fonction (il y a un seul embranchement), et aucun bloc conditionnel n'est présent sur son chemin.
Le code suivant \autoref{cyclomatic-simple-code} a une complexité cyclomatique 1; il s'agit du cas le plus simple que nous puissions implémenter : l'exécution du code rentre dans la fonction (il y a un seul embranchement), et aucun bloc conditionnel n'est présent sur son chemin.
La complexité reste de 1.
\begin{listing}[!hbpt]

View File

@ -121,21 +121,19 @@ Ses deux plus gros défauts concernent sa courbe d'apprentissage pour les nouvea
\caption{\url{https://xkcd.com/1597/}}
\end{figure}
Même pour un développeur solitaire, un système de gestion de versions (quel qu'il soit) reste indispensable, car il permet d'isoler un ensemble de modifications dans une \textit{unité de travail}, jusqu'à ce que celles-ci forment un tout cohérent:
\begin{enumerate}
\item
Même pour un développeur solitaire, un système de gestion de versions (quel qu'il soit) reste indispensable, car il permet d'isoler un ensemble de modifications dans une \textit{unité de travail}, jusqu'à ce que celles-ci forment un tout cohérent:
\begin{enumerate}
\item
Chaque "\textbf{branche}" correspond à une tâche à réaliser: un bogue à corriger (\emph{Hotfix A}), une nouvelle fonctionnalité à ajouter ou un "\emph{truc à essayer}" \footnote{Oui, comme dans "Attends, j'essaie vite un truc, si ça marche, c'est beau."} (\emph{Feature A} et \emph{Feature B}).
\item
Chaque "\textbf{commit}" correspond à une sauvegarde atomique d'un état ou d'un ensemble de modifications cohérentes entre elles.\footnote{Il convient donc de s'abstenir de modifier le CSS d'une application et la couche d'accès à la base de données, sous peine de se faire huer par ses relecteurs au prochain stand-up.}
De cette manière, il est beaucoup plus facile pour le développeur de se concenter sur un sujet en particulier, dans la mesure où celui-ci ne doit pas obligatoirement être clôturé pour appliquer un changement de contexte.
\end{enumerate}
\item
L'historique d'un module est ainsi disponible, sauvé et traçable: qui a réalisé quelle modification à quel moment.
Ceci permet notamment de dessiner l'évolution du code et de mettre un surbrillance que certains modules distincts évoluent un peu trop main dans la main (et devraient donc être refactoriser, selon les principes de développement énumérés plus tôt).
\item
Chaque "\textbf{branche}" correspond à une tâche à réaliser: un bogue à corriger (\emph{Hotfix A}), une nouvelle fonctionnalité à ajouter ou un "\emph{truc à essayer}" \footnote{Oui, comme dans "Attends, j'essaie vite un truc, si ça marche, c'est beau."} (\emph{Feature A} et \emph{Feature B}).
\item
Chaque "\textbf{commit}" correspond à une sauvegarde atomique d'un état ou d'un ensemble de modifications cohérentes entre elles.\footnote{Il convient donc de s'abstenir de modifier le CSS d'une application et la couche d'accès à la base de données, sous peine de se faire huer par ses relecteurs au prochain stand-up.}
De cette manière, il est beaucoup plus facile pour le développeur de se concenter sur un sujet en particulier, dans la mesure où celui-ci ne doit pas obligatoirement être clôturé pour appliquer un changement de contexte.
\end{enumerate}
L'historique d'un module est ainsi disponible, sauvé et traçable: qui a réalisé quelle modification à quel moment.
Ceci permet notamment de dessiner l'évolution du code et de mettre un surbrillance que certains modules distincts évoluent un peu trop main dans la main (et devraient donc être refactoriser, selon les principes de développement énumérés plus tôt).
\begin{figure}[H]
\centering
\scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/diagrams/git-workflow.png}}
@ -181,7 +179,30 @@ De plus, la plupart des plateformes de dépôts présenteront ces informations d
\caption{Un exemple de commit affiché dans Gitea}
\end{figure}
La première ligne est reprise comme titre (normalement, sur 50 caractères maximum); le reste est repris comme de la description.
La première ligne est reprise comme étant le titre (normalement, sur 50 caractères maximum); le reste est repris comme une description (optionnelle).
\begin{minted}{text}
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
\end{minted}
Il est possible de suivre les recommandations de \textit{Conventional Commits} \footnote{\url{https://www.conventionalcommits.org/en/v1.0.0/}}, qui ont pour objectifs de:
\begin{enumerate}
\item
Générer automatiquement un \textit{CHANGELOG}
\item
Déterminer automatiquement des sauts de versions (en se basant sur les types de commits)
\item
Communiquer la nature des changements appliqués au code
\item
Déclencher (automatiquement, toujours) des processus de construction ou de publication
\item
Rendre l'accès au code plus lisible, en facilitant l'exploration du code au travers de commits mieux structurés.
\end{enumerate}
\subsection{Nommer ses branches}