PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Mise à jour

Mise à jour de la base de données, veuillez patienter...

Un peu de fun avec les décorateurs

lundi 17 novembre 2014 à 02:06

Puisque la programmation asynchrone est au goût du jour, on se mange des callbacks un peu partout. Et ça alourdit toujours le code. Chaque langage, lib ou framework a essayé de trouver des astuces pour rendre tout ça plus digeste, et on a vu la naissance des Futures, Deferred, Promises, coroutines, yield from et autres joyeusetés.

Prenons par exemple un script Twisted. Déjà, Twisted, c’est pas vraiment l’exemple de la syntaxe Weight Watcher, ou alors si, mais avant le début du régime.

# -*- coding: utf-8 -*-
 
""" Télécharge des pages et affiche leur, de manière asynchrone """
 
import re
 
# Ceci doit être pip installé
import treq
from twisted.internet.task import react
from twisted.internet.defer import inlineCallbacks, returnValue
 
# Soit on utilise la syntaxe 'inlineCallbacks', c'est à dire avec des yields
# qui marquent les appels asynchrones.
@inlineCallbacks
def get_title(url):
    res = yield treq.get(url) # Ceci est asynchrone et non bloquant
    html = yield res.content() # Ça aussi
    try:
        val = re.search(r'', html.decode('utf8')).groups()[0]
    except:
        val = ''
 
    returnValue(val)
 
# Soit on récupère un objet defer et on ajoute un callback manuellement
def main(reactor):
 
    # Ceci est asynchrone et non bloquant
    defer = get_title('http://sametmax.com/quest-ce-quun-callback/')
 
    # Ceci arrive une fois que get_title est terminé
    def cb(title):
        print(title.upper() + '!')
 
    defer.addCallback(cb)
 
    # Pareil
    autre_defer = get_title('https://github.com/sametmax/django-quicky')
 
    def cb(title):
        print(title.upper() + '!!!')
 
    autre_defer.addCallback(cb)
 
    return defer
 
react(main)

D’une manière générale, je préfère la syntaxe à base de yields, même si elle oblige à se trimbaler le décorateur inlineCallbacks partout, à parsemer sa fonction de yields et à utiliser returnValue à la place de return puisque le mot clé est interdit dans les générateurs en Python 2.7.

Mais bon, ça reste facile à lire. On sait que les lignes avec yield, sont les appels bloquant qu’on demande à la boucle d’événements de traiter de manière asynchrone.

La syntaxe à base de callbacks est plus lourde, en revanche elle donne le contrôle sur la concurrence des callbacks puisqu’ils sont explicites au lieu d’être automatiquement ajoutés par magie. Elle parlera aussi plus aux dev Javascript qui ont l’habitude d’ajouter des callbacks manuellement.

Néanmoins, en JS, on a des fonctions anonymes plus flexibles, et on ferait donc plutôt une truc du genre :

get_title(url).then(function(title){
    # faire un truc avec le résultat
})

Et bien il se trouve qu’avec Python, bien qu’on ne le voit pas souvent, on peut avoir cette idée de la déclaration de son appel asynchrone juste au dessus de son callback, en utilisant des décorateurs.

En effet, les décorateurs ne sont que du sucre syntaxique :

@truc
def bidule():
    chose

N’est en fait qu’un raccourci pour écrire :

def bidule():
    chose
 
bidule = truc(bidule)

Du coup, on peut prendre n’importe quelle fonction, ou méthode, et l’utiliser comme décorateur :

@react
def main(reactor):
 
    then = get_title('http://sametmax.com/quest-ce-quun-callback/').addCallback
    @then
    def cb(title):
        print(title.upper() + '!')
 
    then = get_title('https://github.com/sametmax/django-quicky').addCallback
    @then
    def cb(title):
        print(title.upper() + '!!!')
 
    return cb

Et en jouant avec functools.partial, on peut faire aussi des trucs rigolos.

Non pas que cette syntaxe soit le truc indispensable à connaître et à utiliser. Mais les gens n’y pensent jamais. On utilise pas assez les décorateurs.

Par exemple, combien de fois vous avez vu :

def main():
    print('Doh')
 
if __name__ == '__main__':
    main()

Certaines libs, comme begin, font des décorateurs pour ça :

def main(func):
    if __name__ == '__main__':
        func()

Et du coup, dans son prog:

@main
def _():
    print('Doh')

Comme souvent, c’est le genre de feature qui peut être abusée, mais c’est parfois sympa de rapprocher une action juste au dessus de la fonction qui va être dans ce contexte.

J’espère ainsi vous avoir inspiré pour mettre un hack ou deux en production détournant complètement l’usage des décorateurs et ajoutant quelques gouttes de plus dans le vase de la sécurité de votre emploi, ou votre licenciement.

Pas de bras et pas de chocolat

jeudi 6 novembre 2014 à 09:39

On avait pas d’articles, on a pas de forum, et on aura de nouveau pas d’articles.

C’est à peu près le résumé de ces derniers jours.

Je m’étais lancé dans l’installation de Discourse pour avoir un petit forum pour accompagner le blog, et du coup je n’ai pas écrit.

Mais c’est un FAIL. Et du coup on a pas de forum.

Foxmask et Gordon ont proposé d’installer ça sur leurs machines, du coup on aura peut être quelque chose quand ils auront fait leur bidouille.

Mais je me casse dans un endroit où je n’auras pas accès au téléphone ou internet pendant 10 jours, du coup y aura pas d’articles ces deux semaines non plus.

Du coup je fais un post bien long pour dire qu’il ne se passera rien, si c’est pas du vrai journalisme ça !

Des idées que j’aurai jamais le temps de faire.

mercredi 5 novembre 2014 à 12:01

Ceci est un post invité de Réchèr sous licence creative common 3.0 unported.

Mon cerveau est absolument génial. Il trouve tout le temps un tas de projets super. Le problème c’est que mon corps est une grosse feignasse, et ne prend jamais le temps ni le courage de les réaliser. Du coup, je me retrouve avec un tas de bazar dans la tête dont je ne ferais jamais rien. Le mieux, c’est de l’offrir au monde.

Il y a un peu de tout : du potentiellement intéressant, du bien débile, et du carrément glauque. C’est en vrac, faites en ce que vous voulez.

Et puisque ça semble être une tradition pour les articles longs : un peu de musique.

Chocolate DB

“Maintenant, vous savez sur quoi vous allez tomber”

ipad_chocolate_ichocolates

Repas de Noël en famille. Au moment du café, votre grand-mère pose la traditionnelle boîte de chocolat sur la table. Votre tante est allergique aux noix, votre cousin converti à l’islam ne boit plus d’alcool, y compris sous forme de liqueur, et vous, vous voudriez éviter le caramel car ça colle aux dents. Comme chaque année, votre grand-mère a jeté le papier de description de la boîte, parce que “c’est écrit trop petit dessus”.

Le site de recensement Chocolate DB est là pour vous sortir de ce genre de situation délicate, avec des informations détaillées et individuelles de chaque chocolat, dans chaque boîte. Bien évidemment, le site comporte le bataclan habituel : tags, fonctions de recherche, avis des consommateurs, …

Si ça marche bien, on peut ensuite mettre en place un service d’échange entre personnes habitants à proximité. Deux machins dégeux à la noix de coco contre un succulent praliné au gianduja ? Ça marche !

Le concept est généralisable à tous ce qui se vend sous forme d’assortiment : yaourts aux fruits, sachets de thé, bonbons, bières, …

Pub’homme

toilet-paper-ad

Site web de contre-pouvoir à la publicité.

Au début, il y aurait juste des analyses et des recensements, pour mettre en lumière les techniques de fourbe régulièrement utilisées par la publicité :

Sérieusement, quelqu’un pourrait me dire ce qu’il y a de sexuel dans un putain de clacos ?

Ensuite, il serait envisageable de créer des structures de soutien aux victimes de la publicité. Par exemple :

(Parce que tout le monde n’est pas capable de se défendre aussi bien que les Leneuf).

Pour finir, on pourrait s’offrir quelques flash mobs. 500 personnes se retrouvant à une finale de Rolland-Garros pour balancer des Kinder Buenos à la gueule de Jo-Wilfried Tsonga, ça vous tente ?

Un jeu web avec une économie déflationniste

WoW_stats

Si vous lisez ce blog, vous connaissez déjà les bitcoins et les crypto-monnaies. L’une des raisons pour laquelle ce type de monnaie ne se démocratise pas, c’est qu’elles sont déflationnistes, un fonctionnement assez inhabituel. Un jeu web permettrait de le faire découvrir, d’expérimenter des situations, de détecter des comportements émergents, etc.

Pour le jeu en lui-même, pas la peine de se prendre la tête, il suffirait de reprendre un thème classique : gestion d’une ferme / d’un héros / d’un bar à gigolos … L’économie du jeu aurait les particularités suivantes :

Ce dernier point comporte un risque, car les joueurs pourront décider d’utiliser une ressource de base pour leurs échanges, à la place de la monnaie déflationniste. Par exemple, ils définiront leurs prix en caisse de patates, plutôt qu’en pièces d’or. Ce risque est toutefois limité. Si la caisse de patate devient de fait la monnaie universelle, tous les joueurs chercheront à en fabriquer. Comme c’est une ressource de base, elle est très facile à produire, et elle deviendra donc une monnaie hautement inflationniste, en laquelle il sera difficile de faire confiance. Il y aura peut-être cohabitation de plusieurs monnaies. On ne sait pas, mais je pense que ça vaudrait le coup de tester.

YourCryptoCoins

crypto-coins

Un logiciel simple pour créer sa propre crypto-monnaie, avec pleins de paramètres à ajuster, histoire de tester s’il y a plus efficace que le bitcoin :

Ça permettrait aux gens de se créer leur petite crypto-monnaie locale, ce qui est peut-être un peu plus fiable que des bouts de papier imprimés à l’arrache avec écrit “Soleil” dessus.

Un réseau de communication vraiment décentralisé.

semaphore

Le problème d’internet, c’est que les adresses IP sont attribuées par des autorités centrales. Et le support de communication (les fils téléphoniques et le spectre radio) est également sous contrôle central. Je ne sais pas trop comment y remédier, mais ça mérite réflexion.

On pourrait mettre des capteurs et des petites lumières clignotantes sur les toits des maisons, qui se transmettraient les informations entre eux. Concrètement, vous ne pouriez communiquer qu’avec vos voisins, mais des messages pourraient transiter de maison en maison. Et on utiliserait des uuid pour identifier chaque point du réseau. Avec un système de clé publique-machin-truc pour garantir l’identifiant de chaque point et éviter les usurpations d’identité.

Et pour les zones peu densément peuplée, on utiliserait des successions de petites lumières, ou des fils mais gérés de manière décentralisée.

Ce serait sûrement très lent et pas fiable (si vos voisins n’ont pas allumé leurs transmetteurs, vous ne pouvez plus communiquer). Mais ce serait “libre”. Et du coup, pas d’abonnement à payer, juste de l’électricité.

C’est vraiment brumeux comme idée, parce que j’y connais rien en bidouilleries réseau. Si d’autres gens ont des améliorations à proposer, qu’ils n’hésitent pas.

Poilatout

cousin_machin

Algorithme, qui, à partir d’une phrase donnée, renvoie le “poil au” correspondant.
Exemple :
“Quand l’imbécile montre la Lune”
-> “poil aux burnes”
“Le sage lui met un doigt”
-> “poil au foie”

Y’a plus qu’à plugger ça avec Siri, et votre smartphone vous permettra de spammer toutes vos conversations de “poil au”.

Si ça sert à rien, c’est que c’est indispensable.

Mi-tik

“Trouvez votre moitié”.

half_man_Andy_Gross

Vous êtes unijambiste de la jambe gauche. Vous vous inscrivez sur le site Mi-tik. Vous avez alors la possibilité d’être mis en relation avec des unijambistes de la jambe droite. Si vous trouvez quelqu’un qui vous correspond, vous obtenez 50% de réduction sur tous vos achats de chaussures.

Le site permet également de faire se rencontrer les manchots d’un bras, les borgnes, les “Van Gogh”, les hémiplégiques, …

HarDis (Harcèlement Distribué).

“Pensez dystopie globale, agissez dystopie locale”.

dented-car-chuck-norris

La loi française ne vous interdit pas de chier sur le palier de votre voisin, de rayer sa voiture ou de l’appeler à 2 heures du matin. Mais elle vous interdit de le faire de manière répétée. Ça s’appelle du harcèlement.

Le “réseau d’entraide HarDis” permet de s’affranchir de ce détail. Vous vous y inscrivez en déclarant un périmètre géographique d’action. Vous recevez ensuite des missions, vous demandant d’agir chez une personne-cible. Vous ne recevez jamais deux missions sur une même personne. À chaque fois que vous en effectuez une, vous envoyez une preuve de réalisation (photo, vidéo, …) et vous gagnez des points. Vous pouvez dépenser ces points pour créer des missions sur des gens que vous n’aimez pas.

L’identité et l’adresse des personnes-cibles est révélée le plus tard possible. Chaque compte a un indice de confiance, visible par tous, qui augmente lorsqu’on effectue une mission et diminue lorsqu’on en prend une et qu’on ne l’effectue pas. Le but étant de limiter les “espions” (des personnes qui ne veulent pas réaliser de missions, mais qui veulent juste surveiller si elles ne sont pas la cible de missions existantes).

Bien entendu, vous pouvez acheter des points, et bien entendu tous les outils d’anonymat sont disponibles : réseau Tor, crypto-monnaie, …

ADN-Fuck

“Là où y’a des gènes, y’a du plaisir”.

ensalada-de-pasta

La société ADN-Fuck rachète vos petits déchets corporels : salive, poils, sang, peaux mortes, urine, sperme… Elle les mélange avec ceux de centaines de personnes, et revend le tout sous forme de petit sachet.

Quelqu’un souhaitant commettre un acte illégal peut acheter l’un de ces sachet d’anonymisation, et le répandre sur les lieux de son acte. Bonne chance à la police scientifique pour retrouver l’ADN du coupable !

La société ADN-Fuck en elle-même ne commet rien d’illégal. Le commerce de poils de cul est, à priori, toujours autorisé dans la plupart des régions du globe.

RoboCraft

minecraft_robot

Un jeu vidéo comme Minecraft, mais avec des robots programmables (en python, évidemment !). On peut les faire miner, construire, explorer, transformer des ressources, etc.

Il y a déjà un add-on de Minecraft pour ça (ComputerCraft), mais c’est pas quelque chose de très officiel. Du coup, tout l’équilibrage du jeu est fait sans tenir compte de ComputerCraft. Un jeu conçu spécialement avec des robots, ce serait super chouette.

Ça rejoint le concept d’automatic-play, dont on parle ici ou là : http://gamestudies.org/1301/articles/depaoli_automatic_play

Et il faudrait une économie déflationniste dans le jeu. Et des robots à reconnaissance vocale qui disent “poil au cul”.

Multi-versus

multi_versus

Un jeu vidéo composé de mini-jeux, dans lequel deux joueurs ou plus s’affrontent, chacun avec un mini-jeux différent.

Par exemple, le joueur 1 (Tetris) affronte le joueur 2 (Puzzle Bobble). Lorsque le joueur 1 effectue des actions valorisantes, (détruire plusieurs lignes à la fois), il envoie de la difficulté au Puzzle Bobble du joueur 2 (des bulles noires impossible à détruire). Et vice-versa : si le joueur 2 détruit plus de 3 bulles d’un coup, il envoie des lignes de cochonnerie sur l’aire de Tetris du joueur 1.

Ce n’est faisable qu’avec des mini-jeux ou chacun joue sur son terrain (pas de Bomberman ni de Pong, par exemple), et ce serait une galère monumentale pour les équilibrer entre eux, mais ça serait vraiment fun.

Un format de fichier pour décrire des dessins animés.

Ce serait un format human-readable. C’est très important, car ça augmente la potentialité de bidouillage par n’importe qui, y compris des gamins de 8 ans.
Ça pourrait ressembler à ceci par exemple :

img_balle = http://s2.postimg.org/w709hznpx/balle.png
img_balle_ecrase = http://s18.postimg.org/hy41jochx/balle_ecrase.png
area = 147, 275
default_time = 5 ms

ball = Sprite(img_balle, 20, 0)
ball.move(0, 10)
ball.move(0, 10)
ball.move(0, 10)
ball.change_img(img_balle_ecrase)
ball.move(0, -10)
ball.move(0, -10)
ball.move(0, -10)

Et ça donnerait quelque chose de ce genre :

bounce_147

Et ensuite, on pourrait faire plein de trucs avec ce format de fichier :

Vous allez me dire : “mais il y a déjà plein de trucs qui font ça : le flash, les gifs animés, Blender…”

Oui, sauf que le flash c’est pas libre, les gifs c’est pas optimisé et c’est super compliqué à reprendre et à bidouiller, et Blender, il faut des mois de pratique avant de savoir faire quelque chose de correct.

Ce format de fichier permettrait de créer rapidement des petits trucs à l’arrache, des cartes de vœux, des memes animés, etc. Et comme c’est du texte, c’est versionable et éditable collaborativement.

Githubiser pleins de trucs.

github-octocat

Alors voilà, vous êtes un musicien et vous faites un solo de flûte à bec comme ça pour déconner. Vous le mettez sur gitsound. Quelqu’un d’autre arrive et ajoute une ligne de basse. Un autre y met quelques paroles. Un autre refait votre air initial avec une guitare électrique. Un autre reprend le tout en accéléré. Un autre y ajoute un “Cher effect” et colle un flow de rap par dessus. Et ainsi de suite.

Ou alors, vous êtes un modéliseur 3D et vous commencez à créer un arbre géant. Quelqu’un arrive et ajoute des mini-branches pour lui donner un air plus détaillé. Un autre met des textures plus belles pour les feuilles. Un autre creuse un trou à l’intérieur. Un autre le recopie en 1000 exemplaires sur un quadrillage pour en faire un décor de clip de Mark Gormley. Et ainsi de suite.

Il y a plein d’autres trucs qu’on pourrait githubiser afin de faciliter la création collaborative : les images (vectorielles ou pas), les recettes de cuisine, les règles de jeux de société, les dessins animés, les positions du kama-sutra, … Et pour certains médias, le github de base, qui ne permet de traiter que du texte, ne suffit pas.

Un Tolkien de la culture africaine

blackfoot-medicine-man-right

Tolkien connaissait toutes les légendes de son pays et d’ailleurs. Il a défini la plupart des éléments de l’heroic fantasy. Sans lui, pas de Donjons et Dragons, de World of Warcraft, ou de Harry Potter. Maintenant, l’heroic fantasy vole de ses propres ailes et emprunte des éléments à d’autres cultures : momies égyptiennes, ninjas asiatiques, doppelgängers teutons, mind flayer lovecraftien, …

Mais à part les zombis, on trouve très peu de choses empruntés à la mythologie africaine. C’est vraiment dommage, car avec le foisonnement culturel de ce continent, on pourrait créer des mondes imaginaires de dingue, peuplés de créatures étranges et de magie aux règles complexes, très différent de l’heroic fantasy.

Il faudrait une personne qui connaisse tous ces trucs (l’animisme, le vaudou, les totems, les esprits, …) et qui s’en inspire pour écrire une chiée de bouquins. Ensuite, les rôlistes, les gamers et J.K. Rowling feront le reste.


Voilà, ce sera tout pour le moment. Phosphorez mes braves, phosphorez !! Et si l’une de ces idées existe déjà réellement, prévenez-moi, ça m’intéresse.

Automatiser un peu plus SQLAlchemy declarative

mercredi 29 octobre 2014 à 13:34

Avec l’intégration de l’interface déclarative d’SQLAlchemy, le projet Elexir est mort, et bien mort. Mais avec lui, la syntaxe la plus simple de déclaration pour cet ORM. En effet, par défaut, SQLA vous oblige à spécifier le nom de la table et l’attribut ID pour chaque classe :

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
 
Base = declarative_base()
 
class Person(Base):
    # explicit is better than... fuck it !
    __tablename__ = 'person' 
    id = Column(Integer, primary_key=True)
    name = Column(String(250), nullable=False)
 
class Animal(Base):
    __tablename__ = 'animal'
    id = Column(Integer, primary_key=True) # Paie ton DRY
    name = Column(String(250), nullable=False)
 
engine = create_engine('sqlite:///db.db.db...db')
Base.metadata.create_all(engine)

SQLA est très flexible, et il existe des tas de raisons pour vouloir un PK ou un nom de table custo. Mais dans beaucoup de projets, le nom de la table peut être le nom de la classe, et la PK peut être un ID auto incrémenté.

Heureusement, la lib vous permet de customiser absolument tout, même la manière dont on doit la customiser. Si c’est pas méta, tout ça…

Bref, on peut créer sa propre base déclarative qui va faire tout ça pour nous :

from sqlalchemy.ext.declarative import as_declarative, declared_attr
 
# Ceci sera le parent de tout nos objets
@as_declarative()
class Base(object):
 
    # Vu qu'on a pas vraiment envie de se réécrire la
    # métaclasse, SQLA nous file ce gentil décorateur 
    # pour déclarer des attributs qui ne sont pas des 
    # champs
    @declared_attr
    def __tablename__(cls):
        # le nom de la table est le nom de la classe
        return cls.__name__.lower()
    # L'id c'est la vie
    id = Column(Integer, primary_key=True)
 
class Person(Base):
    name = Column(String(250), nullable=False)
 
class Animal(Base):
    name = Column(String(250), nullable=False)

Evidemment il faut que vous soyez certains d’éviter les conflits liés à cette décision, mais c’est quand même vachement pratique.

AngularJS pour les utilisateurs de jQuery : partie 2

lundi 27 octobre 2014 à 07:53

Après une intro sans trop de code, on va passer à du concret.

Pour apprivoiser la bête, on va comme d’habitude lui faire donner la patte et dire bonjour :

<!doctype html>
<!-- Lien entre l'app et la page -->
<html ng-app="tuto">
  <head>
    <meta charset="utf-8" />
    <title>Hello Angular</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
    <script type="text/javascript">
 
    // Déclaration de l'app
    var app = angular.module('tuto', [])
 
    // Lancement du code quand angular est prêt
    app.run(function(){
        alert('Hello !')
    })
 
    </script>
  </head>
  <body>
  </body>
</html>

Pour comprendre ce snippet, il faut réaliser qu’Angular est d’abord et avant tout un moyen d’organiser son code, et qu’il vous incite à le séparer en conteneurs :

Dans ce code, nous déclarons un module (ou app, c’est kif-kif) nommé “tuto” et n’ayant aucune dépendance (le second paramètre est un array vide) :

var app = angular.module('tuto', [])

Les apps ne font pas grand chose. Ce sont de gros namespaces avec des dépendances, c’est à dire qui déclarent les autres apps nécessaires à leur fonctionnement. Par défaut, aucune autre app n’est obligatoire pour lancer notre code, donc on ne déclare aucune dépendance.

Il y a juste un piège qu’il faut connaître. Si je fais :

var app = angular.module('tuto')

(notez l’absence de second paramètre)

Angular va tenter de me chercher la référence d’app du nom de “tuto” qui existe déjà. L’absence de dépendance se note donc avec un array vide, et non l’absence d’array. Subtilité piégeuse s’il en est. Vu qu’on n’aura pas besoin de déclarer de dépendances avant longtemps, inutile de vous crisper dessus, je vous l’indique juste pour le débuggage qui va immanquablement arriver le jour où vous oublierez l’array vide.

Ensuite, on fait le lien entre notre code HTML et l’app via :

<html ng-app="tuto">

ng-app est ce qu’on appelle une directive. Toutes les directives préfixées ng- sont fournies avec le framework et déjà chargées, prêtes à être utilisées. Une directive est un bout de code Javascript appliqué à du HTML, dans ce cas précis via un attribut.

Vous vous souvenez dans les années 2000 comme on vous faisait chier avec le fait de ne pas mettre de Javascript inline dans du HTML ? Les directives, c’est du Javascript inline qui ne dit pas son nom.

Chaque page doit être liée à une app, et une seule. Si on ne met pas ng-app, notre code ne se chargera pas, si on en met 2, ça foire. C’est le point d’entrée de notre code. Nous déclarons donc que cette page sera gérée par notre app “tuto”.

Puis lance notre hello tonitruant :

    app.run(function(){
        alert('Hello !')
    })

app est la variable contenant la référence à notre app, et en utilisant la méthode .run, on lui passe un callback à appeler quand l’app sera prête.

Il y a plusieurs choses à réaliser ici :

Vous allez me dire, mais espèce de pignouf, pourquoi tu nous montres un code que personne n’utilise ?

Et bien parce que le titre du dossier est AngularJS pour les utilisateurs de jQuery, du coup je vous mets en terrain connu.

Mais la vérité, c’est qu’un vrai hello d’Angular ressemble à ça :

<!doctype html>
<html ng-app="tuto">
  <head>
    <meta charset="utf-8" />
    <title>Hello Angular</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.js"></script>
    <script type="text/javascript">
 
    /* La je recréé l'app à chaque fois pour des raisons de praticité, mais
        dans un vrai code vous auriez ça dans un fichier à part, une seule
        fois */
    var app = angular.module('tuto', [])
 
    // Création d'un controller, et attachement d'une variable au scope.
    app.controller('HelloCtrl', function($scope){
        // Attachement d'une donnée au scope
        $scope.hello = "Hello"
    })
 
    </script>
  </head>
  <body ng-controller="HelloCtrl">
    <!-- Affichage du message attaché au scope -->
    <h1>{{ hello }}</h1>
  </body>
</html>

Et là il y a beaucoup à dire.

Premièrement, contrairement à jQuery, il n’y a pas de notion de préparation du DOM. En vérité, on peut mettre son code Angular en vrac, et Angular va initialiser tout son monde dans le bon ordre, et exécuter ce qu’il faut au bon moment. Et ce bon moment est parfois avant que le DOM soit prêt.

Mais vous n’avez pas à vous en soucier.

Car avec Angular, on manipule très rarement le DOM. Très peu de $('selecteur'), $node.bind() et autre $node.append(), à part dans les directives.

La majorité de votre taf va être de définir des données en pur Javascript, de dire où elles vont dans la page, et de les manipuler en Javascript pendant qu’Angular s’occupe de mettre la page à jour automatiquement.

Nous avons pas mal de nouvelles notions ici. D’abord, le contrôleur…

C’est un bout de code qu’on va lier à un bout de HTML via la directive ng-controller :

  <body ng-controller="HelloCtrl">
    ...
  </body>

Le contrôleur va être responsable de mettre à disposition des données pour ce bout de HTML. On peut lier autant de contrôleurs qu’on veut dans une page, et même les imbriquer. Ici, on dit que notre contrôleur HelloCtrl est responsable de mettre des données à disposition pour le tag body et tout ce qu’il contient.

On fait rarement des contrôleurs qui ratissent aussi large dans de vraies apps, mais pour notre exercice, c’est très bien.

Comment met-on à disposition des données ? Qu’est-ce que veut dire “mettre à disposition” ? Quelles données ?

Notre contrôleur ressemble à ceci :

    app.controller('HelloCtrl', function($scope){
        $scope.hello = "Hello"
    })

La fonction de callback sera exécutée quand ng-controller="HelloCtrl" sera activé automatiquement par Angular. Vous n’avez pas à vous souciez de quand, il le fera au moment le plus optimisé.

La seule chose dont vous avez à vous soucier, c’est ce que vous allez mettre dedans.

Et il y a ici deux nouvelles notions :

L’injection de dépendance fait partie de ces trucs qu’Angular fait automatiquement pour vous. Quand vous déclarez une variable dans le callback d’un contrôleur, Angular va chercher ce nom en mémoire, et récupérer le service qui porte ce nom. S’il ne le trouve pas, il plante.

Une fois qu’il a trouvé le service, il va attendre tranquillement le bon moment pour appeler le callback. Ce moment opportun arrivant, il va lui passer le service en paramètre. On dit qu’il lui “injecte” le service.

En relisant ces paragraphes, je réalise qu’il y a un mélange de plein de termes : contrôleurs, services, injection, bla, bla, bla.

Voilà le deal :

  1. Vous déclarez un contrôleur et vous le liez à du HTML via ng-controlleur.
  2. Dans ce contrôleur vous dites “j’ai besoin du service Machin”
  3. Angular cherche s’il connait Machin. Si non, il plante.
  4. Angular prépare le HTML, décide qu’il est prêt, appelle votre contrôleur et lui passe Machin en paramètre automatiquement.

Les services sont justes des d’objets javascript ordinaires, qu’on a nommés d’une certaine manière pour qu’Angular puisse les injecter. C’est tout.

En effet, pour résoudre le problèmes d’espace de nom en Javascript et d’organisation d’une grosse application, les créateurs d’Angular on créé un grand registre. On enregistre notre code dedans sous un nom (Angular.module('nom_de_l_app'), app.controlleur('nom_du_controller'), etc.), et Angular récupère le bon code au bon moment. C’est essentiellement un moyen de pallier au fait que Javascript est dégueulasse.

Donc, plutôt que de faire des imports comme en Python, on va déclarer une variable en paramètre, et Angular injecte le bon objet pour vous. C’est bizarre, mais on s’habitue.

Du coup, là :

app.controller('HelloCtrl', function($scope){

Je dis “crée moi le contrôleur nommé ‘HelloCtrl’, et quand tu vas appeler le callback, passe lui l’objet nommé ‘$scope’ en paramètre. Fais ça au bon moment, je m’en branle, je veux pas le savoir. Démmerde toi, on a tous des problèmes.”

Le résultat, c’est que votre fonction sera exécutée automatiquement au bon moment, et qu’elle aura $scope qui lui sera passé.

Alors à quoi sert ce $scope ?

C’est simple : tout ce qui est attaché en attribut de $scope est accessible dans le HTML géré par ce contrôleur. Quand je fais :

    app.controller('HelloCtrl', function($scope){
        $scope.hello = "Hello"
    })

Et que après je lie mon contrôleur au HTML :

  <body ng-controller="HelloCtrl">
    <!-- Affichage du message attaché au scope -->
    <h1>{{ hello }}</h1>
  </body>

Ma variable hello est disponible ici (via les petites moustaches qu’on verra au prochain chapitre), parce que je suis dans body qui est géré par le contrôleur HelloCtrl qui contient un $scope avec l’attribut hello. Fiou ! Ca en fait des couches. Mais Angular, c’est comme un orgre un onion.

Vous touchez ici du doigt l’essence même du boulot des contrôleurs, et à peu près leur unique but : mettre des données à disposition du HTML. C’est le C de MVC.

C’est pour cette raison que je vous ai répété “Angular va tout faire au bon moment, vous n’avez pas à vous soucier de quand”. Ceci est en effet très frustrant à entendre quand on vient du monde de jQuery puisque qu’on doit savoir exactement quand on fait les choses : il faut que le DOM soit prêt, il faut que les éléments sur lesquels on travaille existent, il faut qu’on puisse récupérer des nodes et en ajouter.

Avec Angular, on ne fait rien de tout ça. On va déclarer ses données dans le contrôleur, et dire où elles doivent être mises dans la page. Angular se charge de faire le lien pour vous quand le HTML est prêt, quand le Javascript est chargé, quand les Dieux sont avec vous…



Télécharger le code de l’article.