3 Revīzijas 1edf6ab82d ... a7e7d002c4

Autors SHA1 Ziņojums Datums
  Benoît Hubert a7e7d002c4 Exemple section 4.3 7 gadi atpakaļ
  Benoît Hubert 6fddf2c9c8 Refonte 4.1 et 4.2 7 gadi atpakaļ
  Benoît Hubert 6fd648d68c Intro et exemple TDD 7 gadi atpakaļ

+ 19 - 0
_stuff/Random Stuff.md

@@ -21,3 +21,22 @@ function count_words(str) {
 }
 ```
 
+
+
+
+
+
+
+
+
+On passe à la suite !
+
+Je t'entends demander : « Quoi, déjà fini ? Ça y est, je suis un Maître Jedi du Python ? »
+
+Heu, ne t'emballe pas, jeune padawan ! Disons que tu en sais assez pour passer à la suite !
+
+Un petit rappel tout de même, s'il ne fallait retenir qu'une chose :
+
+> **Attention** à l'indentation : une erreur d'indentation peut empêcher le programme de se lancer !
+
+Prochaine étape : un mini-serveur web en Python !

+ 92 - 1
css/prism.css

@@ -1,5 +1,5 @@
 /* PrismJS 1.11.0
-http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+python */
+http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+python&plugins=line-highlight+line-numbers */
 /**
  * prism.js default theme for JavaScript, CSS and HTML
  * Based on dabblet (http://dabblet.com)
@@ -138,3 +138,94 @@ pre[class*="language-"] {
 	cursor: help;
 }
 
+pre[data-line] {
+	position: relative;
+	padding: 1em 0 1em 3em;
+}
+
+.line-highlight {
+	position: absolute;
+	left: 0;
+	right: 0;
+	padding: inherit 0;
+	margin-top: 1em; /* Same as .prism’s padding-top */
+
+	background: hsla(24, 20%, 50%,.08);
+	background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
+
+	pointer-events: none;
+
+	line-height: inherit;
+	white-space: pre;
+}
+
+	.line-highlight:before,
+	.line-highlight[data-end]:after {
+		content: attr(data-start);
+		position: absolute;
+		top: .4em;
+		left: .6em;
+		min-width: 1em;
+		padding: 0 .5em;
+		background-color: hsla(24, 20%, 50%,.4);
+		color: hsl(24, 20%, 95%);
+		font: bold 65%/1.5 sans-serif;
+		text-align: center;
+		vertical-align: .3em;
+		border-radius: 999px;
+		text-shadow: none;
+		box-shadow: 0 1px white;
+	}
+
+	.line-highlight[data-end]:after {
+		content: attr(data-end);
+		top: auto;
+		bottom: .4em;
+	}
+
+.line-numbers .line-highlight:before,
+.line-numbers .line-highlight:after {
+	content: none;
+}
+
+pre.line-numbers {
+	position: relative;
+	padding-left: 3.8em;
+	counter-reset: linenumber;
+}
+
+pre.line-numbers > code {
+	position: relative;
+    white-space: inherit;
+}
+
+.line-numbers .line-numbers-rows {
+	position: absolute;
+	pointer-events: none;
+	top: 0;
+	font-size: 100%;
+	left: -3.8em;
+	width: 3em; /* works for line-numbers below 1000 lines */
+	letter-spacing: -1px;
+	border-right: 1px solid #999;
+
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+
+}
+
+	.line-numbers-rows > span {
+		pointer-events: none;
+		display: block;
+		counter-increment: linenumber;
+	}
+
+		.line-numbers-rows > span:before {
+			content: counter(linenumber);
+			color: #999;
+			display: block;
+			padding-right: 0.8em;
+			text-align: right;
+		}

+ 6 - 0
css/style.css

@@ -165,3 +165,9 @@ span.breadcrumb::before {
 .about img {
   max-height: 48px;
 }
+
+.page p > code,
+.page strong > code {
+  background: rgba(27,31,35,0.05);
+  color: rgba(112, 0, 0, 1);
+}

+ 28 - 0
examples/03-classes-starwars.py

@@ -0,0 +1,28 @@
+class ForceSensitive:
+    capacites_base = ["soulever des objets"]
+
+    def __init__(self, nom, capacites=[]):
+        self.nom = nom
+        self.capacites = self.capacites_base + capacites
+
+    def __str__(self):
+        return "Je suis {nom} et je peux :\n  - {capacites}\n".format(nom=self.nom, capacites="\n  - ".join(self.capacites))
+
+class Jedi(ForceSensitive):
+    capacites_base = ["soulever des objets", "combattre au sabre laser", "influencer les esprits"]
+
+class JediBadass(ForceSensitive):
+    capacites_base = ["soulever des objets", "combattre au sabre laser", "influencer les esprits", "réapparaître comme fantôme"]
+
+class Sith(ForceSensitive):
+    capacites_base = ["soulever des objets", "combattre au sabre laser", "envoyer des éclairs"]
+
+leia = ForceSensitive("Leia Organa Solo", ["jouer à Mary Poppins"])
+rey = Jedi("Rey", ["à peu près tout faire en fait"])
+luke = Jedi("Luke Skywalker", ["projeter un hologramme", "entrer en lévitation"])
+obiwan = JediBadass("Obiwan Kenobi", ["balancer des punchlines de ouf"])
+empereur = Sith("L'Empereur", ["dissimuler la Force", "manipuler et trahir"])
+snoke = ForceSensitive("Leader Suprême Snoke", ["envoyer des éclairs", "skyper par télépathie"])
+
+for personnage in [leia, rey, luke, obiwan, empereur, snoke]:
+    print(personnage)

+ 18 - 0
examples/debut.py

@@ -0,0 +1,18 @@
+nom = input("Quel est ton nom ?\n")
+
+# Remarque 1: les valeurs booleennes en Python sont True et False
+# Remarque 2: pas de do/while en Python => break pour quitter la boucle
+while True:
+    age = input("Quel âge as-tu ?\n")
+
+    # Equivalent du try/catch en Java, JavaScript, PHP...
+    try:
+        age_converti = int(age)
+    # Si la valeur saisie n'est pas convertible en entier,
+    # une exception de type ValueError est generee
+    except ValueError:
+        print("La valeur '%s' n'est pas entière, recommence!" % (age))
+
+    break
+
+print("Tu t'appelles {nom} et tu as {age} ans.".format(nom=nom, age=age))

+ 32 - 0
examples/functions.py

@@ -0,0 +1,32 @@
+# Renvoie la date passée en paramètre, augmentée d'un mois
+def meme_jour_mois_prochain(date):
+
+	# Découpe la chaîne et renvoie une liste
+	# (équivalent des tableaux en Java, JS...)
+	jour_mois_annee = date.split('/')
+
+	# On devrait avoir 3 éléments exactement
+	if len(jour_mois_annee) != 3:
+		raise ValueError("La date attendue est au format JJ/MM/AAAA\n")
+
+	# On tente de convertir les éléments en entiers
+	try:
+		jour = int(jour_mois_annee[0])
+		mois = int(jour_mois_annee[1])
+		annee = int(jour_mois_annee[2])
+
+		if mois == 12:
+			annee += 1
+			mois = 1
+		else:
+			mois += 1
+
+		return "{:02d}/{:02d}/{:04d}".format(jour, mois, annee)
+
+	except ValueError:
+		raise ValueError("La date attendue est au format JJ/MM/AAAA\n")
+
+# Verifie que ça marche
+print(meme_jour_mois_prochain("12/02/2018"))
+print(meme_jour_mois_prochain("27/12/2018"))
+print(meme_jour_mois_prochain("27/toto/2018"))

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3 - 1
js/prism.js


+ 29 - 13
react-tuto/src/markdown/4. Bases de Python/01. Premier programme.md

@@ -1,23 +1,39 @@
-Jeune padawan, prépare-toi à devenir un Jedi du Python !
+Dans ton dossier `python-tdd/4-bases`, il y a le le fichier `01-nom-age.py`, avec le contenu suivant :
 
-Pour cela, dégaîne tes armes l'éditeur de texte, et le terminal.
+```python
+nom = input("Quel est ton nom ?\n")
 
-Dans ton dossier `python-tdd/bases`, crée le fichier `01-premier.py`, avec le contenu suivant :
+# Remarque 1: les valeurs booleennes en Python sont True et False
+# Remarque 2: pas de do/while en Python => break pour quitter la boucle
+while True:
+    age = input("Quel âge as-tu ?\n")
 
-```python
-message = "Hello galaxy"
-print(message)
+    # Equivalent du try/catch en Java, JavaScript, PHP...
+    try:
+        age_converti = int(age)
+    # Si la valeur saisie n'est pas convertible en entier,
+    # une exception de type ValueError est générée
+    except ValueError:
+        print("La valeur '%s' n'est pas entière, recommence!" % (age))
+
+    break
+
+print("Tu t'appelles {nom} et tu as {age} ans.".format(nom=nom, age=age))
 ```
 
-Puis dans le terminal (marche sous OS X, Linux *et* Windows/PowerShell) :
+Puis dans le terminal (Remplacer **python** par **python3** sous OS X et Linux) :
+
+```sh
+cd ~/CHEMIN/VERS/DOSSIER/python-tdd/4-bases
+python 01-nom-age.py
+```
 
-    cd ~/CHEMIN/VERS/python-tdd/bases
-    python 01-premier.py
+Quelques remarques... À propos, c'est le moment de **prendre des notes** ! Ça pourrait ressortir dans le QCM...
 
-Cela produit l'affichage de `Hello galaxy` dans le terminal. Ce petit programme nous permet déjà de faire trois constats.
+> Comme PHP ou JavaScript, Python est un langage de script, **interprété**, et n'a pas besoin de passer par une phase de compilation.
 
-> Python est un langage de script, et n'a pas besoin de passer par une phase de compilation.
+> Tu te souviens de `int unEntier;` en Java, ou `var uneVariable;` en JavaScript... Vois-tu la différence ici ?... On **ne déclare pas les variables** en Python, on les affecte directement.
 
-> Contrairement à JavaScript (souviens-toi du <code><strong>var</strong> variable</code> en JS), on **ne déclare pas** les variables en Python, on les affecte directement.
+> Autre chose qui doit te sembler étrange : où sont les accolades `{}` pour délimiter le corps de la boucle `while`, et ceux du `try` et du `catch` ? Disparues ! En Python, c'est **le carcactère `:` et l'indentation** qui permettent de déterminer le début et la fin d'un bloc d'instructions. Tout ce qui se trouve entre `age = ...` et `break` forme le corps du `while`. De la même façon, ici `try` et `except` ont chacun un bloc d'instructions constitué d'une seule ligne.
 
-> Il n'est pas nécessaire de mettre des points-virgule à la fin des lignes. Ils sont acceptés, mais en pratique, *personne* dans la communauté Python ne les utilise. Leur seule utilité éventuelle est de séparer des instructions sur une même ligne.
+> Il n'est pas nécessaire de mettre des points-virgule à la fin des lignes. Ils sont acceptés, mais en pratique, *personne* dans la communauté Python ne les utilise. Leur seule utilité éventuelle est de séparer des instructions sur une même ligne.

+ 35 - 22
react-tuto/src/markdown/4. Bases de Python/02. Fonctions.md

@@ -1,34 +1,47 @@
 Voici maintenant un exemple de fonction en Python (crée le fichier `02-fonctions.py`) :
 
 ```python
-# Salue quelqu'un en utilisant son titre
-def greet(name, gender=''):
-    if gender == 'male':
-        title = 'Mr'
-    elif gender == 'female':
-        title = 'Ms'
-    else:
-        title = 'Mx'   # titre "neutre"
-    print("Hello %s %s" % (title, name))
-
-greet("Johnson", "male")
-greet("May", "female")
-```
+# Renvoie la date passée en paramètre, augmentée d'un mois
+def meme_jour_mois_prochain(date):
 
-Pour saluer (*greet*) une personne, on utilise le "titre honorifique" suivi du nom. Le titre est déterminé à partir du genre.
+	# Découpe la chaîne et renvoie une liste
+	# (équivalent des tableaux en Java, JS...)
+	jour_mois_annee = date.split('/')
 
-Remarques :
+	# On devrait avoir 3 éléments exactement
+	if len(jour_mois_annee) != 3:
+		raise ValueError("La date attendue est au format JJ/MM/AAAA\n")
 
-> On définit une fonction en précédant son nom du mot-clé `def`.
+	# On tente de convertir les éléments en entiers
+	try:
+		jour = int(jour_mois_annee[0])
+		mois = int(jour_mois_annee[1])
+		annee = int(jour_mois_annee[2])
 
-> Python **n'utilise pas** la syntaxe `{}`, commune à C/C++, Java, PHP, JavaScript, etc. pour ouvrir et fermer le corps d'une fonction (ou un bloc de code en général)
+		if mois == 12:
+			annee += 1
+			mois = 1
+		else:
+			mois += 1
 
-> Au lieu de cela, on ouvre un bloc de code en utilisant `:` et en laissant **au moins un espace d'indentation** au début de chaque ligne du bloc. Toutes les lignes d'un *même bloc* doivent avoir le même *niveau* d'indentation (même nombre d'espaces avant)
+		return "{:02d}/{:02d}/{:04d}".format(jour, mois, annee)
 
-Cette dernière particularité de Python est absolument fondamentale ! Python se base sur l'*indentation* pour détecter ce qui appartient à un même bloc.
+	except ValueError:
+		raise ValueError("La date attendue est au format JJ/MM/AAAA\n")
 
-> Essaie de voir ce qui se passe en "désindentant" la ligne `title = 'Mr'` pour la ramener au niveau du `if` qui la précède. **Tu risques de rencontrer cette erreur à nouveau si tu n'es pas rigoureux sur l'indentation**.
+# Verifie que ça marche
+print(meme_jour_mois_prochain("12/02/2018"))
+print(meme_jour_mois_prochain("27/12/2018"))
+print(meme_jour_mois_prochain("27/toto/2018"))
+
+```
+
+Remarques :
+
+> On définit une fonction en précédant son nom du mot-clé `def`.
 
-Un avantage d'abandonner les `{}` est que le code est moins "chargé" visuellement. Il n'y a pas d'*obligation* quant au nombre d'espaces d'indentation, mais une *convention* très répandue, qu'en pratique tout le monde adopte, préconise l'usage de 4 espaces entre chaque niveau d'indentation (vrais espaces et non tabulation).
+> Le corps de la fonction est, comme les blocs `while`, `try`, `catch` de l'exemple précédent, déterminé par les `:` ouvrants suivis d'un bloc indenté
 
-Dernière chose, les arguments de `print()` : La partie entre guillemets ressemble à ce qu'on pourrait trouver en C ou en Java : `%s` et `%d` sont des "emplacements" pour des variables (chaîne et entier respectivement). Par contre, la syntaxe de la suite diffère : les valeurs à insérer dans les emplacements sont situées entre parenthèses, suite au caractère `%`.
+> Un avantage d'abandonner les `{}` est que le code est moins "chargé" visuellement. Il n'y a pas *d'obligation* quant au nombre d'espaces d'indentation, ni par rapport à d'autres aspects comme le nommage des variables et des fonctions. Ceci dit, la communauté Python utilise des *conventions* très largement adoptées, qui évitent que chacun(e) adopte sa propre convention. Par exemple :
+- on préconise l'usage de 4 espaces entre chaque niveau d'indentation (vrais espaces et non tabulation).
+- les noms des fonctions et variables utilisent la convention [Snake case](https://fr.wikipedia.org/wiki/Snake_case) c'est à dire des `mots_minuscules_separes_par_underscore`.

+ 0 - 11
react-tuto/src/markdown/4. Bases de Python/05. Conclusion provisoire.md

@@ -1,11 +0,0 @@
-On passe à la suite !
-
-Je t'entends demander : &laquo;&nbsp;Quoi, déjà fini ? &Ccedil;a y est, je suis un Maître Jedi du Python ?&nbsp;&raquo;
-
-Heu, ne t'emballe pas, jeune padawan ! Disons que tu en sais assez pour passer à la suite !
-
-Un petit rappel tout de même, s'il ne fallait retenir qu'une chose :
-
-> **Attention** à l'indentation : une erreur d'indentation peut empêcher le programme de se lancer !
-
-Prochaine étape : un mini-serveur web en Python !

+ 19 - 0
react-tuto/src/markdown/4. Bases de Python/05. Intro au TDD.md

@@ -0,0 +1,19 @@
+On a suffisamment de bases pour entrer dans le vif du sujet, et aborder le TDD, ou Test-Driven Development, ou en français Développement piloté par les tests.
+
+> C'est la seule page de théorie pure... Si ça te semble encore un peu trop, n'hésite pas à passer à la section suivante pour un **exemple**.
+
+Le TDD s'intègre dans les "méthodes agiles". Il aide à répondre à la question : **comment développer une application à la fois fiable et maintenable ?**
+
+<img src="https://media.giphy.com/media/g8XkcuerwzVS0/giphy.gif" alt="collapse" style="float: left; padding: 0 15px 15px 0;" />
+
+C'est une question sensible quand le développement s'étale sur des années ! Bien des **projets** informatiques se sont **terminés en désastres** techniques et financiers, faute de pouvoir **maintenir et faire évoluer** le code.
+
+En quoi ça consiste ? Eh bien, c'est en quelque sorte une inversion du cycle "traditionnel" de développement dans les (grosses) entreprises. Avant, on passait du temps à écrire une "spec" parfois massive, détaillant tout ce que devait faire un logiciel. Mais cet effort de réflexion ne permettait pas toujours de prévoir certains "pépins" rencontrés dans la pratique.
+
+Le TDD a une portée résolument *pratique* et utilise quelques concepts simples :
+- Avant d'écrire du code pour *faire* quelque chose, on écrit *d'abord* un test qui va **vérifier que le code qu'on écrit va se comporter comme on l'a prévu**.
+- Au lieu de planifier énormément de choses en amont, on cherche d'abord à créer un **produit minimal** utilisable par des utilisateurs. A partir de cette base, on ajoute des fonctionnalités de façon *incrémentale*, par petites touches.
+
+Le fait d'**écrire les tests en premier** a une conséquence logique : quand on lance les tests juste après les avoir écrits, ils échouent ! Et c'est voulu ! Une fois qu'on a un test qui échoue, on écrit le code qui permet de faire passer le test.
+
+D'une certaine façon, les tests servent à la fois de spécification et de documentation du code. Ecrire des tests peut nous aider à clarifier notre intention avant de se lancer tête baissée dans le code.

+ 104 - 0
react-tuto/src/markdown/4. Bases de Python/06. TDD - exemple 1.md

@@ -0,0 +1,104 @@
+Un exemple : on veut écrire une fonction, qui nous renvoie une valeur booléenne indiquant si on est majeur quand on a l'âge passé en paramètre.
+
+#### Le test
+
+D'abord le test :
+
+```python
+import unittest
+
+class TestIsMajor(unittest.TestCase):
+
+    def test_is_major(self):
+        self.assertTrue(is_major(30))
+        self.assertFalse(is_major(11))
+
+if __name__ == '__main__':
+    unittest.main()
+```
+
+La classe `TestIsMajor` hérite de `TestCase` et de ses méthodes. Parmi celles-ci, des *assertions*, qui permettent de s'assurer qu'on obtient bien le résultat voulu.
+Les deux utilisées ici, `assertTrue()` et `assertFalse()`, veulent s'assurer que l'expression qu'elles reçoivent en paramètre est respectivement vrai ou faux.
+
+Les assertions sont souvent groupées à plusieurs dans une méthode, mais on aurait pu aussi bien remplacer `test_is_major()` par deux méthodes distinctes :
+
+```python
+    def test_is_major(self):
+        self.assertTrue(is_major(30))
+
+    def test_is_not_major(self):
+        self.assertFalse(is_major(11))
+
+```
+
+#### Première exécution : échec attendu
+
+Si on l'exécute tel quel (`python 06-tdd-exemple1.py`), on obtient logiquement une erreur car la fonction `is_major()` n'existe pas :
+```text
+E
+======================================================================
+ERROR: test_is_major (__main__.TestIsMajor)
+----------------------------------------------------------------------
+Traceback (most recent call last):
+  File "06-tdd1-test.py", line 10, in test_is_major
+    self.assertTrue(is_major(30))
+NameError: name 'is_major' is not defined
+
+----------------------------------------------------------------------
+Ran 1 test in 0.000s
+
+FAILED (errors=1)
+```
+A noter : le `E` isolé en première ligne nous indique que l'unique test a fini en *erreur* (ce qui est confirmé par la dernière ligne avec `errors=1`).
+
+#### Petite amélioration...
+
+Tentons une première amélioration, et ajoutons ces deux lignes juste au-dessous de la classe, dans le même fichier (normalement on a les **tests et le code à tester dans des fichiers séparés**) :
+
+```python
+def is_major(age):
+    pass
+```
+
+Voyons ce qui se passe :
+```text
+F
+======================================================================
+FAIL: test_is_major (__main__.TestIsMajor)
+----------------------------------------------------------------------
+Traceback (most recent call last):
+  File "06-tdd1-test.py", line 10, in test_is_major
+    self.assertTrue(is_major(30))
+AssertionError: None is not true
+
+----------------------------------------------------------------------
+Ran 1 test in 0.000s
+
+FAILED (failures=1)
+```
+
+Cela ne semble pas beaucoup mieux, et pourtant, on progresse... Au moins, le test a pu appeler la fonction `is_major()`.
+
+Au lieu d'un `E` isolé en première ligne, on a un `F` pour *failure*. On nous indique en bas `failures=1`.
+
+On a une `AssertionError` car la première assertion a échoué. La deuxième aussi aurait échoué, mais une méthode dans une classe de test est interrompue à la première assertion qui échoue.
+
+#### Résolution :
+
+Voici le code qui fera passer le test :
+```python
+def is_major(age):
+    return True if age >= 18 else False
+```
+
+Résultat :
+```text
+.
+----------------------------------------------------------------------
+Ran 1 test in 0.000s
+
+OK
+```
+
+&Ccedil;a a marché, et le succès est matérialisé par le `.` isolé sur la première ligne (il n'y avait qu'un test).
+Cette convention (`.` pour succès, `F` pour échec, `E` pour erreur) donne un affichage compact, utile quand on a *beaucoup* de tests à faire passer.

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 1
react-tuto/src/resources/markdown.json