PROJET AUTOBLOG


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

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

⇐ retour index

0bin a été migré

jeudi 25 septembre 2014 à 10:09

Vous vous souvenez de 0bin, le paste bin chiffré ? Il est de nouveau en ligne sur 0bin.net.

Je suis en train de copier tous les pastes en ce moment, et il y en a 28436 donc rsync rame un peu (j’ai fait l’erreur de le faire avec scp au début…) du coup tous les pastes ne seront pas là instantanément, mais au final, on les retrouvera tous.

Bref, on répare doucement mais sûrement.

Un bot qui tweete 4992 Super-Héros à raison de un toutes les 3,14 heures peut-il sauver Internet ?

mercredi 24 septembre 2014 à 08:43

Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Depuis l’Antiquité, en moyenne, environ une seule personne s’est posée cette question, dont moi.

Je me propose donc de partager avec vous mon expérience sur le sujet en vous expliquant ce que j’ai mis en œuvre pour tenter d’obtenir une réponse.

Bonsjours, je voudrais tweeter en Python, s’il vous plaît.

Il existe plusieurs bibliothèques en Python qui papotent avec l’API de Twitter. Neuf sont actuellement référencées par Twitter.

Parce qu’il y avait écrit “Actively maintained”, j’ai tenté twython .

sudo pip install twython

Il se trouve qu’elle fait très bien ce que j’attendais d’elle, donc je ne suis pas allé chercher plus loin.

Par ailleurs, le code pour envoyer un tweet est flippant de simplicité :

from twython import Twython
 
APP_KEY = "votre petite clef-clef à vous"
APP_SECRET = "une autre petite clef-clef"
OAUTH_TOKEN = "encore une petite clef-clef"
OAUTH_TOKEN_SECRET = "enfin, une dernière petite clef-clef"
 
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
 
texte = 'Oh mon Dieu ! Mais cette librairie crache des flammes ! #Twython'
 
twitter.update_status(status=texte)

Et paf :

 

Pour obtenir ses petites clefs-clefs, il est nécessaire de se créer un compte sur la plate-forme de Twitter réservée aux développeurs.

Je vous la fait courte :

> My applications dans le menu de votre icône en haut à droite > Create New Apps
> Remplir le formulaire > accepter les conditions d’utilisation après les avoir lues
> Permissions > Read and Write
> API Keys > Generate my acces token

La partie la plus compliquée pour moi fut de passer les permissions en “Read and Write“.

En effet, il faut associer un numéro de téléphone au compte twitter utilisé et la manipulation doit être faite à partir du téléphone en question.
Or, je n’ai pas de téléphone portable.

Heureusement, la femme de ma Vie a eu la bonne idée de faire l’Amour il y a 17 ans et sa fille, la fille de ma Vie, possède ce genre d’appareil.
Sauvé.

Si vous n’avez pas de fille de votre Vie, ni même de femme de votre Vie, faites en sorte d’avoir un téléphone portable.

(J’ai mis Amour et Vie en gras pour gagner des points de Karma. Je ne sais pas si ça marche, mais je me dis que ce serait tout de même bien malheureux que ça ait un effet négatif.)

Bonsjours, je voudrais ajouter une image au tweet que je tweete en Python, s’il vous plaît.

En partant du principe que vous avez une image.png à côté de votre script :

from twython import Twython
 
APP_KEY = "votre petite clef-clef à vous"
APP_SECRET = "une autre petite clef-clef"
OAUTH_TOKEN = "encore une petite clef-clef"
OAUTH_TOKEN_SECRET = "enfin, une dernière petite clef-clef"
 
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
 
texte = 'Mais quelle folie ! #Twython tweete aussi des images ! '
image = open("image.png", 'rb')
 
twitter.update_status_with_media(status=texte, media=image)

Et hop :

 

J’ai du mal à imaginer qu’on puisse faire plus simple.

Maintenant, j’aimerais tweeter toutes les 3,14 heures. C’est possible ?

Pour tweeter toutes les 3 heures, une petite crontab réglée de la sorte aurait fait l’affaire :

0 */3 * * * python chemin/vers/mon/script.py

Mais pour 3,14 heures, il faut faire dans le délicat.
Je fais donc patienter le script 11 304 secondes entre chaque envoi :

from time import sleep
 
while True:
 
	envoie_moi_donc_un_tweet_avec_une_image_de_Super_Heros_du_Net()
	sleep(11304)

Tout à fait cochon.
Tout à fait parfait.

Et pour les 4992 Super-Héros.
Comment fait-on ?

Alors là, vous n’allez pas être déçus.

Afin de d’obtenir 4992 Super-Héros, je me suis dit qu’il me suffisait de trouver 24 têtes de Super-Héros, 13 torses de Super-Héros et 16 jambes de Super-Héros puis de réaliser toutes les combinaisons possibles de ces trois éléments.

24 * 13 * 16 = 4992

Hasard de folie, il se trouve qu’afin de récolter des fonds pour mener à bien leurs actions, La Quadrature Du Net propose une petite appli de personnalisation de Super-Héros pour celles et ceux qui font un don.

Et bien, aussi incroyable que cela puisse paraître, elle propose de choisir parmi 24 têtes de Super-Héros du Net, 13 torses de Super-Héros du Net, 16 jambes de Super-Héros du Net.

L’émotion est un peu retombée, mais, sur le moment, j’ai eu la chaire de poule face à une telle coïncidence, surtout quand on sait que La Quadrature défend les droits et les libertés des citoyens sur Internet depuis 2008 et que c’est pile-poil dans la thématique de la question qui fait l’objet de cet article.

Me voilà donc parti pour trouver un moyen de récupérer tous ces morceaux de Super-Héros du Net et de reconstituer les 4992 Super-Héros entiers possibles.

Pour retrouver l’adresse de chaque morceau, un simple coup d’œil au code-source de la page a suffi.

Ensuite, en terme de n’importe quoi je vous propose un morceau de choix.

Car pour faire ma petite popote, j’ai utilisé Processing.

_ Processing ?!?!
_ Oui. Processing.
_ Mais… ça utilise une syntaxe Java, ça. Non ?
_ Oui. Mais pour Sam&Max j’ai utilisé le mode Python qui vient tout juste de sortir.
_ Utiliser un “framework Java” en Python. Excuse-moi, mais c’est un peu n’importe quoi.
_ Tout à fait.

Pour celles et ceux qui ne le connaissent pas, Processing est un logiciel/langage conçu pour que les artistes, les graphistes, les designers et les grosses bubuses comme moi puissent facilement et rapidement produire du code créatif (image, son, vidéo).

Je ne vais pas vous faire un cours. Il y a un FLOSS Manual en français pour ça.

Utilisant Processing depuis plusieurs années, je vous avoue que le mode Python m’a clairement donné l’impression de faire du skate avec des rollers, mais comme outils pédagogique, ça doit être pas mal.
Et puis ça marche !

Voici le code que j’ai pondu pour l’occasion :

# La fonction setup() est propre à Processing.
# Elle s’exécute en premier et une seule fois.
 
def setup():
 
    # On crée une image transparente de 1 pixel de côté qui sera utilisée
    # pour représenter l’absence de choix de tête, de torse et de jambes
    img_vide = createImage(1, 1, ARGB)
    img_vide.pixels[0] = color(0, 0)
 
    # On crée des listes qui contiendront les images des têtes, torses et jambes
    # et on place l'image vide crée juste avant comme premier élément.
    global liste_torses, liste_tetes, liste_jambes
    liste_tetes = [img_vide]
    liste_torses = [img_vide]
    liste_jambes = [img_vide]
 
    # On lance le téléchargement de toutes les images nécessaires.
    telechargement_images()
 
    # On fabrique les 4992 Super-Héros avec les images téléchargées.
    usine_super_heros()
 
def telechargement_images():
 
    # Sachant que chaque image est atteignable par une URL du type
    # http://soutien.laquadrature.net/images/bonus/head_01.png
 
    # On définit l'URL du dossier où sont situées les images.
    url = "http://soutien.laquadrature.net/images/bonus/"
 
    # On télécharge l'image du Super-Héros tout nu qui est placé au fond
    # de chaque image et qui sert de base à tous les autres.
    global base
    base = loadImage(url + "base.png")
 
    # On télécharge les 24 têtes, les 13 torses et les 16 jambes.
    choix_image(url, "head_", liste_tetes, 24)
    choix_image(url, "torso_", liste_torses, 13)
    choix_image(url, "legs_", liste_jambes, 16)
 
def choix_image(url, nom, liste, nb_image):
 
    # Pour chaque nombre entre 1 et nb_image (0 correspondant à img_vide),
    for i in range(1, nb_image):
 
        # on définit un numéro toujours composé de 2 chiffres. Ex: 05 pour 5,
        numero = str(i).zfill(2)
        # on récupère l'image en recomposant l'URL,
        image_recuperee = loadImage(url + nom + numero + ".png")
        # et on ajoute cette image dans la liste correspondante.
        liste.append(image_recuperee)
 
def usine_super_heros():
 
    # Sachant que toutes images récupérées ont la même taille (214x383)
    # et qu'elles sont des .png transparents dont le contenu trouve sa place
    # quand les images sont parfaitement superposées .
 
    # On définit une variable qui va compter les 4992 Super-Héros.
    compteur = 0
 
    # Pour toutes les images de têtes,
    for tete in liste_tetes:
 
        # pour toutes les images de torses,
        for torse in liste_torses:
 
            # et pour toutes les images de jambes,
            for jambe in liste_jambes:
 
                # on crée un élément graphique de 214x383,
                super_hero = createGraphics(214, 383)
                # on démarre le dessin de l'élément graphique,
                super_hero.beginDraw()
                # on place un fond blanc,
                super_hero.background(255)
                # on place le Super-Héros tout nu aux coordonnées 0, 0
                super_hero.image(base, 0, 0)
                # on place l'image des jambes en 0, 0
                super_hero.image(jambe, 0, 0)
                # on place l'image du torse en 0, 0
                super_hero.image(torse, 0, 0)
                # on place l'image de la tête en 0, 0
                super_hero.image(tete, 0, 0)
                # on clos le dessin de l'élément graphique,
                super_hero.endDraw()
                # on sauvegarde notre Super-Héros du Net dans le dossier data,
                super_hero.save("data/SuperHeros-{0}.png".format(compteur))
                # et on incrémente le compteur de 1 avant de passer
                # au Super-Héros suivant
                compteur += 1

Et hop !

Et sinon, c’est encore loin Grand Schtroumpf ?

On arrive au bout.

Pour ne pas tweeter les Super-Héros du Net dans l’ordre et pour ne pas non plus poster deux fois le même en cas de croutage du serveur, je me suis créé un petit ordre.txt comme cela :

from random import shuffle
 
tous_les_numeros = range(4992)
shuffle(tous_les_numeros)
 
with open ("ordre.txt", 'w') as base:
 
    for numero in tous_les_numeros:
 
        base.write("{0},".format(numero))

Et dont le contenu est juste :

1798,915,1402,1032,507,112,1098,600,323,787,1435,200,774,33,1419,106,1836... etc

Enfin, voici le script final, qui tweete donc toutes les 3,14 heures un des 4992 Super-Héros du Net.

# -*- coding: utf8 -*-
 
# Ce script permet d'envoyer un tweet avec une image toutes les 3,14 heures
# Il récupère dans une liste le numéro de l'image a envoyer,
# et enregistre le nombre de tweets déjà postés
# pour pouvoir changer d'image à la prochaine exécution
 
import os
from twython import Twython
from time import sleep
 
# Clef d'authentification à obtenir sur https://dev.twitter.com
APP_KEY = "votre petite clef-clef à vous"
APP_SECRET = "une autre petite clef-clef"
OAUTH_TOKEN = "encore une petite clef-clef"
OAUTH_TOKEN_SECRET = "enfin, une dernière petite clef-clef"
 
twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
 
ordre = []
combien_deja_poste = 0
 
# Récupère le répertoire courant
chemin_dossier = os.path.dirname(os.path.realpath(__file__))+"/"
 
def envoie_moi_donc_un_tweet_avec_une_image_de_Super_Heros_du_Net():
 
    # Récupère l'ordre des images à poster
    with open (chemin_dossier+"ordre.txt", 'r') as contenu:
 
            ordre = contenu.readline().rstrip('\n').split(",")
 
    # Récupère le nombre d'images déjà postées
    with open (chemin_dossier+"deja_poste.txt", 'r') as contenu:
 
        combien_deja_poste = int(contenu.readline().rstrip('\n\r'))
 
    # Récupère le numéro de l'image
    numero = int(ordre[combien_deja_poste])
 
    # Construit le chemin vers cette image
    # Sachant que le dossier AllStar contient les 4992 Super-Héros du Net
    chemin_image = chemin_dossier+"AllStar/SuperHeros-{0}.png".format(numero)
 
    # Charge l'image
    image = open(chemin_image, 'rb')
 
    # Forme le texte du tweet
    texte = ("Voici le Super-Héros du Net n°{0}\n"
             "Si ce n'est pas déjà fait, aidez-le, lui et ses 4992 amis\n"
             "soutien.laquadrature.net").format(numero+1)
 
    # Envoie le tweet composé d'un texte et d'une image
    twitter.update_status_with_media(status=texte, media=image)
 
    # Met à jour le nombre d'images déjà postées
    with open (chemin_dossier+"deja_poste.txt", 'w') as contenu:
 
        contenu.write(str(combien_deja_poste + 1))
 
while True:
 
    envoie_moi_donc_un_tweet_avec_une_image_de_Super_Heros_du_Net()
 
    # Patiente pendant les 3,14 heures, soit  les 11 304 secondes
    sleep(11304)
 
    # Le script plantera dans 2 ans quand les 4992 Super-Héros du Net
    # aurons tous été postés, et c'est très bien ainsi...

Et voilà, il ne reste plus qu’à choper 10 Millions de followers sur ce compte et à espérer que 3,14% d’entre eux filent 10€ à LQDN pour que l’on puisse augmenter les chances de répondre positivement à la question posée dans le titre.
Une simple formalité…

Je précise que La Quadrature n’a absolument rien à voir avec ce compte Twitter, c’était juste l’occasion pour moi de pondre un article de plus ici tout en soutenant une cause qui me tient hackeur (humour).

À la prochaine…

Information complémentaire :
Le Monde en date du 12 août – Près d’un compte Twitter sur 10 est alimenté automatiquement

Le blog a été migré (enfin presque)

mardi 23 septembre 2014 à 14:18

Max a pris l’hébergement et a setup Varnish (sans lequel le WP s’écroule sous la masse de vos pattes velues), puis j’ai installé WP 4, et migré la DB et les images.

En gros, vous pouvez de nouveau lire vos articles techniques préférés. Et les autres aussi.

Par contre, feu notre ancien thème (snif, il était si… si… attachant…), et donc tous les easter eggs qui vont avec.

Vous noterez donc un blog moins fun, avec des bugs graphiques et surtout, sans plugins, donc ouvert aux 4 vents aux spams mais sans formulaire de contact. Mouarf.

Je vais corriger ça petit à petit, mais je ne me presse pas, l’avantage d’écrire un blog bénévole est qu’on ne doit rien à personne, donc on peut se gratter les couilles.

0bin, multiboards et allthatcount vont suivre. Je vais probablement les migrer avant de m’attaquer à la corrections des petits problèmes du blog d’ailleurs.

La base de données complète a été migrée. Félicitation à l’équipe de WP pour avoir mis en œuvre un outil de conversion des tables d’une version à l’autre parfaitement transparent. C’est pas facile à faire, et ça marche super bien. Mettez l’article en favoris, je ne dirais pas souvent du bien de cet outil.

Ça signifie que vos commentaires sont là, les pages statiques aussi, et bien entendu, pour les contributeurs, leurs comptes utilisateurs également. Il y a eu un bug de login ce matin, mais c’est réparé.

Si vous voyez des trucs qui pètent, merci de les lister en comment ici que je puisse avoir une check list à jour et sans doublons. Avec les URLS c’est mieux.

Parmi les problèmes que je vois venir : les iframes qui marchent pas, la barre de menu de droite qui déconne, et autres joyeusetés, des médias de mauvaise taille.

Et bonne lecture :)

Qu’est-ce qu’une API ?

samedi 6 septembre 2014 à 21:13

L’API, pour Application Programming Interface, est la partie du programme qu’on expose officiellement au monde extérieur pour manipuler celui-ci. L’API est au développeur ce que l’UI est à l’utilisateur : de quoi entrer des données et récupérer la sortie d’un traitement.

L’API au sens original

Initialement, une API regroupe un ensemble de fonctions ou méthodes, leurs signatures et ordre d’usage pour obtenir un résultat.

Par exemple, imaginons que je fasse une lib pour botter des culs en Python, bottage.py :

def senerver(moment):
    # ...
 
def botter(cul):
    # ...
 
def init():
    # ...
 
if __name__ == "__main__":
    init()

Je vais l’utiliser ainsi :

from bottage import senerver, botter
 
senerver(now)
botter(le_cul_de_ce_con)

Les deux fonctions senerver() et botter() sont mes points d’entrée pour cette action. Je n’utilise pas init(), qui est un code interne à la lib et ne me regarde pas.

Donc leurs noms et leurs paramètres ainsi que leurs types sont l’API de ma lib, ce qui m’est exposé pour l’utiliser.

Si on veut rentrer dans des subtilités, on dira en fait que senerver() et botter() font partie de l’API publique, c’est à dire de ce qui est manipulable par un utilisateur de la lib. A l’inverse, init() fait partie de l’API privée, c’est à dire ce qui est manipulable par les développeurs de la lib. Mais quand on parle d’API sans préciser, on parle de l’API publique.

Changement d’API

En informatique, on peut généralement exposer les choses de plusieurs manières différentes. Je peux changer mon API :

import datetime
 
def senerver(moment=None):
    if not moment:
        moment = datetime.datetime.utcnow()
    # ...
 
def botter(cul):
    # ...
 
def init():
    # ...
 
if __name__ == "__main__":
    init()

Ici, j’ai changé mon API pour rendre le paramètre moment facultatif afin de faciliter la vie des utilisateurs de la libs.

Et là on aborde un point très important du concept : la stabilité d’une API.

Puisque l’API est ce qu’on expose au monde extérieur, le monde extérieur va l’utiliser d’une certaine façon. Si on change cette manière de l’utiliser dans une version suivante, au moment de la mise à jour, on va casser leur code si on ne fait pas attention.

Par exemple, ici je rends un paramètre facultatif : ça ne craint pas grand-chose. Mais si j’avais fait l’inverse ? J’avais un paramètre facultatif, et soudain je le rends obligatoire. Toutes les personnes qui n’ont pas passé le paramètre vont soudain avoir un plantage s’ils passent à la nouvelle version de la lib car l’API a changé.

C’est donc une seconde définition de l’API : l’API est une promesse, un contrat entre l’auteur d’un code et ceux qui vont utiliser ce code. Cette promesse est “voici ce que vous pouvez utiliser sereinement dans votre programme, je ne vais pas tout péter demain”.

Cette promesse est plus ou moins bien respectée selon les projets. Python, par exemple, a un historique exemplaire de stabilité d’API, et n’a cassé la compatibilité qu’une fois, avec Python 3, donnant 10 ans aux développeurs pour s’adapter.

Dans tous les cas, si une lib est beaucoup utilisée et que son développeur a le sens des responsabilités, elle évolue plus doucement. Pour cette raison, il faut faire attention au choix qu’on fait dans le style de son API, sous peine de ne pas pouvoir le changer plus tard.

En effet, on peut tout à faire écrire le même code dans des tas de styles différents. Ainsi, je pourrais botter des culs avec une API orientée objet :

 
class Colere:
 
    @classmethod
    def global_init():
        # ...
 
    def __init__(moment):
        # ...
        self._senerver(moment)
 
    def _senerver():
        # ...
 
    def botter(cul):
        # ...
 
if __name__ == "__main__":
    Colere.global_init()

Mon bottage de cul n’a plus du tout le même goût à l’usage :

from bottage import colere
c = Colere(now)
c.botter(un_aperi_cul)

Mon programme fait la même chose, mais mon API est différente. Notez le _senerver() qui est préfixé d’un underscore, une convention en Python pour dire que cette méthode doit être considérée comme ne faisant pas partie de l’API publique, donc à ne pas utiliser depuis l’extérieur. En effet, il n’y a pas de méthode privée en Python.

Qualités d’une API

On a vu que la stabilité était une qualité importante d’une API. Mais il y en a d’autres. Notamment la performance et l’ergonomie, généralement deux notions qui s’affrontent.

Pour l’ergonomie, il s’agit de rendre facile les usages qu’on fait le plus couramment, et rendre possible les usages les plus ardus. Prenez l’exemple d’une requête HTTP avec paramètre POST sur un site qui a besoin de cookies d’une requête précédente. Pas un usage incroyablement complexe a priori…

Avec la stdlib de Python, ça donne ça :

import urllib
import urllib2
import cookielib
 
cookie_jar = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar))
urllib2.install_opener(opener)
urllib2.urlopen('http://site.com')
 
data = urllib.urlencode({"nom": "valeur"})
rsp = urllib2.urlopen('http://site.com/autre/page', data)
print(rsp.read())

La même chose avec la lib requests :

import requests
 
request.get('http://site.com')
res = request.post('http://site.com/autre/page', data={"nom": "valeur"})
print(res.content)

Il ne s’agit pas juste du fait qu’il y ait beaucoup moins de lignes pour le faire. La facilité à découvrir comment faire dans le deuxième cas est exemplaire : en lisant, on comprend le code. En bidouillant dans le shell, on peut sans doute trouver tout ça. On n’a pas besoin de se poser la question de ce qu’est une jar, l’url encoding, etc.

Le premier exemple est non seulement verbeux, mais très, très difficile à trouver soi-même. En fait, même avec la doc sous les yeux, ce n’est pas évident d’arriver à ce résultat, et on y arrivera après des essais douloureux.

Le deuxième exemple est plus ergonomique que le premier.

Mais l’ergonomie a généralement un coût : celui de la performance.

Imaginez que j’ai la lib de bottage de fion sous forme fonctionnelle :

import datetime
 
def senerver(moment=None):
    if not moment:
        moment = datetime.datetime.utcnow()
    # ...
 
def botter(cul):
    # ...
 
def init():
    # ...
 
if __name__ == "__main__":
    init()

Je veux la rendre plus ergonomique. Je sais qu’il faut obligatoirement s’énerver pour botter un cul, et je décide donc de cacher cette fonction et l’appeler automatiquement :

import moment
 
def _senerver(moment=None):
    if not moment:
        moment = datetime.datetime.utcnow()
    # ...
 
def botter(cul, moment=None):
    _senerver(moment)
    # ...
 
def init():
    # ...
 
if __name__ == "__main__":
    init()

Dans la plupart des cas, ça va aider mon public :

Au lieu de devoir savoir qu’il faut s’énerver avant de botter, ils ont juste à botter :

from bottage import botter
botter(cul_de_jatte)

J’ai identifié que c’était l’usage le plus courant, donc c’est une amélioration. Mais ça a un prix pour une petite partie de mes utilisateurs : les très gros botteurs de cul. Ceux qui bottent des culs par centaine.

Avant, ils pouvaient faire :

from bottage import senerver, botter
senerver()
for cul in rang_doigons:
    botter(cul)

Mais maintenant, faisant :

from bottage import botter
for cul in rang_doigons:
    botter(cul)

Ils ont un appel à _senerver() à chaque tour de boucle, et donc un appel à datetime.utcnow() aussi !

Bien entendu, il est possible de remédier à cette situation, mais cet article n’est pas là pour vous expliquer comment créer une belle API. Ce serait néanmoins un très bon article.

Ici, je vous montre simplement qu’en facilitant, on suppose d’un usage, et ça peut se faire au détriment des autres. L’automatisme a tendance à retirer de la marge de manœuvre.

Une bonne API va donc proposer un moyen automatique de faire les opérations de tous les jours, va lui donner une forme (nom, ordre des actions, organisation, etc) ET un moyen de faire des choses compliquées ou performantes. Ce qui va rendre l’API plus riche, donc plus lourde, avec une plus grosse doc, etc.

Tout a un coût.

API Web

Jusqu’ici vous avez vu l’API d’une bibliothèque, mais il existe d’autres genres d’API. L’un est devenu particulièrement populaire depuis le milieu des années 2000 : l’API Web.

L’API Web est comme l’API précédente ce qui est exposé à l’extérieur pour manipuler un programme. Entrées. Sorties. Mais il y a plusieurs différences :

Il existe de nombreux protocoles qui permettent de faire une API Web : SOAP, REST, XML RPC, WAMP etc.

Aujourd’hui, les API Web les plus populaires utilisent majoritairement un protocole pseudo-REST avec en encoding JSON.

Hum, je vous vois sourciller.

Oui, c’est clair que la phrase est un peu tordue du cul, comme si elle avait été bottée.

Prenons donc un exemple : un service Web de bottage de cul !

Vous êtes donc asskicker.io, leader mondial du bottage de cul en ligne. Et vous exposez votre processus de bottage de cul exclusif à tous les programmeurs.

Pour ce faire, vous mettez à disposition une API WEB sous forme pseudo REST. Au lieu d’appeler des méthodes, les développeurs vont envoyer des requêtes HTTP Get et Post à des URLs représentant les culs à botter.

Je ne vais pas rentrer dans les détails de ce qu’est du (pseudo) REST ou JSON exactement, mais un exemple de requêtes à faire pour botter des culs via notre API Web serait :

import json
import requests
 
# On fait une requête GET vers l'URL du service pour obtenir de quoi s’énerver
colere = requests.get('http://asskicker.io/colere/')
 
# Je créer un nouveau bottage de cul
data = json.dumps({'cul': 'de bouteille', 'colere': colere['id']})
headers = {'content-type': 'application/json'}
res = requests.post('http://asskicker.io/bottage/', data=data, headers=headers)

Et supposons qu’on veuille connaître le dernier bottage de cul fait :

res = requests.get('http://asskicker.io/bottage/last')
print(res.json()) # et la réponse JSON du service :
# {
#     "bottage": 89080,
#     "cul": "de bouteille",
#     "colere": 99943,
#     "date": "2014-09-06 20:38:11"
# }

Les URLs sont fictives, complètement inventées, et ne correspondent à rien.

Ici, notre API est donc la collections d’URLs (http://asskicker.io/bottage/, http://asskicker.io/bottage/last, etc.) qui permet de manipuler notre service, ainsi que les nom set types des paramètres à envoyer via data et le contenu de la réponse.

Le but de l’API Web est de permettre de manipuler du code sur une machine distante à travers le Web, depuis n’importe quel langage capable d’envoyer une requête HTTP. L’API Twitter permet de de lister des tweets et en envoyer. Par exemple, si on est authentifié, faire une requête GET sur http://api.twitter.com/1.1statuses/show/787998 permet d’obtenir en retour un JSON contenant les informations sur le tweet numéro 787998

L’API Google permet de faire des recherches. L’API flicker permet d’uploader des photos. Toutes les APIS ont des formes différentes, certaines sont plus ou moins faciles, plus ou moins efficaces, utilisent tels ou tels formats, mais au final, c’est la même chose : un moyen de manipuler le service en faisant des requêtes.

La manière classique de créer un site est de générer le HTML final sur le serveur. Or, comme il est possible d’envoyer des requêtes HTTP depuis une page Web en utilisant Ajax, on voit aujourd’hui des sites codés en Javascript qui vont chercher leurs données sur le serveur via l’API du site. Le navigateur reçoit ainsi un HTML incomplet, et le JS appelle l’API pour reconstruire la page.

Ainsi, on code la logique une seule fois : récupérer les informations, effectuer des actions… Et on utilise l’API pour tout ça, que ce soit pour faire le site Web, ou pour laisser d’autres programmeurs utiliser le site.

Évidemment, une vraie API Web est complexe, possède des problématiques de sécurité, d’authentification… Encore un bon article à écrire.

AngularJS pour les utilisateurs de jQuery : partie 1

mardi 2 septembre 2014 à 11:55

Après avoir sondé un peu Twitter, le sujet intéresse, et il est très, mais alors, très mal traité ailleurs sur le net. Malgré mon aversion, bien connue, pour javascript, je m’en bouffe beaucoup, prog Web oblige.

Angular rend l’utilisation de JS plus supportable, mais possède une très grosse courbe d’apprentissage bien pentue, avec des ressources vraiment mal foutues.

Et surtout, le problème vient de l’habitude de jQuery.

AngularJS est l’anti-jQuery, ça ne fonctionne pas du tout pareil, mais personne n’est là pour vous montrer comment faire la transition. Ce sera le but de ce dossier, un dossier qui va être long car ce framework est un gros morceau.

Quand utiliser AngularJS ?

Tous les sites ne sont pas faits pour être créés avec Angular. En effet, si votre site est essentiellement composé de contenu statique, avec juste un peu de dynamique pour quelques widgets, Angular n’a aucun intérêt.

Angular est lourd à mettre en œuvre, on va donc se faire chier à sortir le bazooka quand on monte au front, pas quand on fait une garde de nuit en rase campagne.

Malgré le fait que Google interprète maintenant les pages en JS, le texte reste roi, et Angular vous force à faire des pages en pur JS, rendant le référencement aléatoire de votre contenu. Les hacks que l’on voit un peu partout sur la toile pour compenser cela sont très loin d’obtenir l’effet d’un référencement naturel de contenu facilement accessible à l’ancienne.

En fait, le plus gros défaut d’Angular, c’est qu’il ne permet pas, pour le moment, de faire de la dégradation gracieuse. Soit le mec a Javascript et il a accès au site. Soit le site est illisible.

jQuery a donc encore de beaux jours devant lui.

Typiquement, on ne prendra pas Angular pour :

Ce sont des sites dont le contenu est important. On veut que les moteurs de recherche puisse les indexer de fond en comble. On veut que ce soit lisible sans JS et par les aveugles (sauf le tube vidéo :-D). Les ajouts de JS se feront par petites touches.

On utilisera plutôt Angular pour :

Ce sont des sites pour lesquels l’ergonomie est plus importante que le contenu. Le public est captif, et on peut exiger Javascript, prix à payer pour une expérience utilisateur moderne.

Vous noterez qu’il y a une certaine opposition entre vieux format (wiki, forum, blog) et format contemporain (app, réseau social). Ce n’est pas un hasard. Les premiers sont orientés contenu, les seconds orientés usage. C’est ainsi que le Web a évolué, et Angular est là exactement pour répondre à cette évolution.

Il est néanmoins tout à fait possible de faire des choses hybrides. Rien n’empêche de faire des parties du sites statiques, et d’autres très dynamiques, de mélanger les deux sur une même page. Par exemple, sur un blog, vous pouvez mettre l’article en statique pour le ref et la facilité de consultation, et la partie commentaires en Angular, pour améliorer l’expérience utilisateur.

Que va apporter Angular ?

Les avantages sont doubles.

D’abords, il y a les avantages pour vous, le développeur. Angular vous force en effet à utiliser un style Javascript très propre qui tend à découpler les composants, isoler les services, lisser les angles et refaire le papier peint à fleur. L’application en devient plus propre, plus facile à faire évoluer et à maintenir. Toutes les bonnes pratiques d’organisation d’une app JS ont une réponse angularesque. Ce n’est pas forcément celle que vous voulez, mais elle marche.

Ensuite, Angular va vous rendre plus productif. Pas tout de suite, évidement, coincés que vous êtes dans votre logique jQuerinne. Mais une fois le cap passé, coder beaucoup de JS sera immensément plus simple et plus rapide, car Angular vous épargne beaucoup de logique répétitive.

En prime, ce framework est orienté tests. Ce n’est pas obligatoire, mais c’est bien plus facile qu’avec des modules jQuery.

Mais il y a également les avantages pour l’utilisateur. Puisque qu’Angular est complètement dynamique, il bénéficiera automatiquement d’une app plus réactive, avec moins de rechargements, plus de fonctionnalités aussi. En effet, vous rechignerez moins à rajouter cette fonction “delete-row” si vous savez que ça vous prend 3 lignes de code.

Attention cependant, il y a aussi des inconvénients.

Angular est lourd, c’est long à charger, ça bouffe de la ressource pour chaque tab, et si vous avez beaucoup de traitements sur une page, vous avez intérêt à optimiser votre race le bouzin sinon ça va ramer. Tout le monde n’est pas sur une machine de dev propre à 8 cœurs avec une ligne ADSL qui a tout compris.

En fait, si votre site est destiné à être consulté ainsi : je cherche, je consulte du contenu, et je m’en vais, charger Angular est un pur gâchis. Il faut au moins que l’on interagisse un peu avec le site pour que ça vaille le coup : créer du contenu, manipuler des informations, se faire notifier, etc.

Et puis il y a la tentation, la tentation forte de tout faire à la main côté client. Avec son lot de problèmes de sécurité (le delete qui fait pas de vérif), de merdier dans l’histo de navigation (le clic du milieu qui marche pas, le back qui back pas où on veut), les sessions qui font des siennes (je suis déco, mais y mon avartar là, pourquoi ?), etc.

Angular n’est pas une solution pour le bidouilleur Javascript de devenir soudainement grand programmeur Web, et ne dispense pas de coder correctement.

Si je fais de l’Angular, je dois faire du NodeJS ?

Non.

Angular est une techno parfaitement neutre, elle est limitée au client, et par conséquent, vous êtes libres de choisir votre techno côté serveur : .Net, PHP, Ruby, Java, Python…

Par contre, pour les tests, les outils sont très orientés autour de l’écosystème NodeJS : npm, grunt, PhantomJS, etc. Il va falloir mettre les mains dans le cambouis, avec son lot de trucs qui s’installent pas, de dépendances mal branlées, de doc pas à jour, de code JS peu expressif et tout le bordel. C’est tout le problème du JS, la communauté a gardé le côté crade et à l’arrache du langage. Heureusement, ils ont récupéré les designers du monde Ruby, donc vous aurez des tutos obsolètes, mais très jolis.

Ok, mais je peux faire quoi avec ?

Car finalement, quand on n’a pas utilisé Angular, on se demande finalement qu’est-ce que ça apporte par rapport à jQuery.

Essentiellement, c’est que ça divise par 100 les manipulations du DOM. Avec Angular, on met ses données dans un tableau d’objets JS, on indique où les afficher, et on manipule ensuite notre array. Le DOM est mis à jour sans qu’on doive s’en soucier.

Ça à l’air de rien comme ça, mais si vous ouvrez vos fichiers JS avec jQuery, vous verrez qu’ils sont blindés de .click()code et de .append(). C’est la majorité du code ! Code qu’on a pas besoin d’écrire avec Angular.

L’organisation d’Angular invite aussi naturellement à écrire un code linéaire. Pas de blocs dans des blocs dans des blocs, chaînes de callbacks et amas de closures. Il y en a toujours, mais bien moins.

Après, on fait finalement la même chose qu’on ferait avec jQuery, mais plus facilement. Ce qui explique qu’on fait rarement une app full JS avec jQuery, ce qui est infernal à coder, alors qu’on le fait systématiquement avec Angular, car c’est son milieu naturel.

Ce qui va vous surprendre avec cette façon de faire, c’est que tout ce qui s’affiche dans la page passe par Angular. En fait, on a généralement une API REST, et Angular en front pour afficher le site. Pas trop de templating côté serveur.

Et on verra des exemples concerts de tout ça dans les parties suivantes :)