Blog // Exirel.me

Retrouvez tous les articles liés au tag Technique via le flux rss dédié à ce tag.

Identité, authentification, et autorisation

Par Florian Strzelecki - 00:31 - 05.08.2015

Tags : Web, Technique, Vulgarisation, Sécurité, Identité, Authentification, Autorisation

La gestion de l'identité et des comptes utilisateurs est un problème très courant dans le développement web : nous construisons des interfaces de gestion, des "back offices" pour les utilisateurs, et des "dashboards" pour les opérateurs ; bref, nous donnons un accès privilégié à certains utilisateurs.

Pour autant, je suis toujours un peu surpris du manque de culture autour de ces trois notions, qui ne sont que rarement vues comme trois domaines distincts - ce qu'elles sont pourtant. Alors, une fois n'est pas coutume, j'ai envie d'aborder ce sujet là, en essayant de rester simple.

Identité

L'identité d'une personne, c'est qui elle est. Par exemple, je suis Florian Strzelecki, et je suis aussi @Exirel. Ce sont des identités : quelque part, ce sont les données qui existent sur moi. Ces données peuvent être plus ou moins publiques, et peuvent être partagées avec plus ou moins de degrés de confidentialité.

Lorsqu'Amazon affiche mon nom en haut à gauche ou dans mon panier d'articles, c'est mon identité que j'ai chez Amazon qu'il utilise.

Par nature, l'identité n'est pas une clé ni une autorisation : je peux toujours prétendre être quelqu'un d'autre, il me faut plus qu'une identité pour avoir de la crédibilité.

Authentification

C'est là qu'entre en jeu l'authentification : c'est le fait de garantir l'authenticité des informations fournies. De dire : je suis bien le "moi" que je dis être.

Par exemple, toujours sur Amazon, lorsque je souhaite passer au paiement d'une commande, je dois prouver que je suis bien moi, en fournissant mon identifiant de compte, et le mot de passe associé. J'apporte la garantie de l'authenticité de mes propos lorsque je dis que je suis bien moi, en utilisant une information qui n'est connue que de moi seul (à noter qu'Amazon ne possède pas mon mot de passe, mais le moyen de vérifier que celui que je donne correspond bien à l'information qu'il possède).

Cependant, rien ne dit que l'authentification a besoin de l'identité : imaginez que vous vous rendez à un club privé où il faut fournir un mot de passe. Dans ce cas, rien ne dit qui vous êtes, votre mot de passe ne fait que dire que vous êtes un utilisateur authentifié - ou une utilisatrice authentifiée, il n'y a pas de raison d'exclure quelqu'un s'il ou elle a le mot de passe.

C'est d'autant plus important de comprendre la différence entre l'identité et l'authentification, car l'un est public (ou partageable), et l'autre doit rester secret. Prenez la biométrie : bien que théoriquement unique et personnel, les informations restent publiques. Il suffit de laisser une trace de doigt sur une porte, et il y a au moins votre ophtalmologiste pour connaître les motifs de vos yeux. Et pour un peu, votre ADN est déjà dans un fichier de police...

Ainsi, la biométrie n'est pas un bon moyen de s'authentifier : c'est, au mieux, un excellent moyen de définir une identité, aussi bien qu'un identifiant de compte.

Il y a, bien entendu, d'autres applications à cette différence : vous pouvez très bien obtenir un jeton d'authentification (ou "token" en Anglais) pour accéder à un service, tout en restant anonyme.

Autorisation

Le dernier point est le plus facile à distinguer des deux autres : il s'agit de définir ce à quoi une personne (identifiée ou non) a droit ou non, ce à quoi elle a accès ou non.

Amazon me laisse choisir des produits et payer ma commande, mais il ne me permet pas d'avoir accès aux commandes des autres utilisateurs.

L'autorisation cependant a besoin soit de l'identité, soit de l'authentification :

Pour reprendre l'exemple du club privé, chaque membre qui a un mot de passe a exactement les mêmes droits (et devoirs) que les autres. Il n'y a pas de différence entre l'identité de l'un et de l'autre.

Sécurité et données

Lorsque l'on parle de sécurité, ces trois notions se retrouvent très vite sur la table : que ce soit pour accéder à des données ou à des services. Il est important de comprendre chaque concept, et de comprendre où et comment il s'applique à chaque situation - et à chaque besoin.

Par exemple, certaines informations sont liées à l'identité d'une personne (son nom, son identifiant, etc.), alors que d'autres ne sont liées qu'à son ordinateur ou à son téléphone (l'IP, le navigateur, l'emplacement géographique). Le traitement sera alors différent, et les techniques pour assurer la sécurité du système le seront tout autant.

De même, bien séparer l'identité de son authentification permet de faire évoluer l'un et l'autre séparément : peu importe si vous utilisez HTTP ou HTTPS pour connaître votre prénom, alors qu'il vaut mieux chiffrer les mots de passe dans une base de données (pour que cela reste un secret).

Bref, ce ne sont que quelques explications (un peu imprécises, je m'en excuse) sur ces trois notions, et j'espère que vous serez moins perdu la prochaine fois que quelqu'un vous parlera de l'authentification et des autorisations associées à votre identité.

HTTP Accept header

Par Florian Strzelecki - 21:45 - 25.04.2014

Tags : Web, HTTP, Hypermedia, Développement, HATEOAS, REST, Technique, Programmation, Bonne pratique, API

La RFC 2616 de l'IETF est une spécification du protocole HTTP/1.1. Ce document décrit notamment les headers des requêtes HTTP à disposition des clients, et en particulier le header "Accept", qui sera le sujet de cet article.

À quoi sert-il ?

En résumé : il permet la négociation de contenu dirigée par le client.

En utilisant Accept, le client indique au serveur HTTP quels types de contenus il est capable de gérer, éventuellement avec un ordre de préférence. Le serveur est alors invité à fournir le type de contenu le plus adapté pour répondre à la demande du client (mais ce n'est pas obligatoire).

Ce paramètre peut être utilisé avec d'autres headers, comme Accept-Language ou Accept-Encoding ; par exemple, le serveur Apache (qui documente sa méthode de négociation de contenu) utilise plusieurs headers pour déterminer la meilleure réponse possible.

Comment ça marche ?

Prenons un exemple avec une requête envoyée par Firefox (28.0), qui utilise ce header par défaut :

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Ceci indique au serveur que le client attend, par ordre de préférence :

Pour le cas d'une balise img, le header sera différent, et contiendra plutôt image/png (j'ai d'ailleurs remarqué que l'URL n'est absolument pas prise en compte par le navigateur pour générer son header).

Les versions et les API (partie 2)

Par Florian Strzelecki - 17:23 - 16.01.2014

Tags : Web, Bonne pratique, Technique, API, Version, HTTP, Hypermedia

Si vous ne l'avez pas déjà fait, je vous invite à lire la partie 1, qui parle d'applications installées plusieurs fois, dans des environnements clients spécifiques à chacun, et où, finalement, le numéro de version dans l'URL n'a pas beaucoup de sens.

Sommaire

  1. Partie 1 : L'application fournie un service via une interface (ou plusieurs)
  2. Partie 2 : Le service est fourni par une (ou plusieurs) applications

Le service est fourni par une (ou plusieurs) applications

Dans la partie 1 donc, j'ai présenté un exemple de SIGB, qu'il faut installer dans un environnement client. Cette fois, je vais prendre le même SIGB, mais avec un seul serveur centralisé : vous êtes alors en position de fournisseur de service, comme peut l'être Facebook, Twitter, Google, et bien d'autres.

Dans ce contexte, il existe des différences fondamentales avec le contexte précédent :

  1. Chaque version de l'application n'est installée qu'une seule fois (par vous)
  2. Les mises à jour sont indépendantes de la volonté des clients
  3. Chaque client décide à quel moment il exploite un changement rétro-compatible
  4. Chaque changement non rétro-compatible impose une adaptation du client (immédiate ou différée)

Les contraintes et les attentes n'étant pas les mêmes, la notion de version peut être traitée différemment.

Les versions et les API (partie 1)

Par Florian Strzelecki - 00:00 - 16.01.2014

Tags : Programmation, Bonne pratique, Technique, API, Version

Vaste sujet que la gestion des versions dans le domaine des API, et cette fois encore ce billet fait suite à une discussion que j'ai eu sur Twitter. Tout a commencé par un tweet d'@edasfr (compte protégé), dont vous pouvez trouver le blog dont je conseille la lecture à cette adresse : n.survol.fr.

Comme les tweets ne suffisent pas, voici un billet qui, je l'espère, vous éclairera sur la gestion des versions et des URLs pour vos (futures) API.

Je ne prétends ni avoir la réponse, ni détenir de vérité absolue : par mon partage d'expérience j'espère seulement vous donner les bonnes informations pour que vous répondiez vous-même à la question.

Sommaire

  1. Partie 1 : L'application fournie un service via une interface (ou plusieurs)
  2. Partie 2 : Le service est fourni par une (ou plusieurs) applications

Mais de quoi est-il question ?

Une API, ce n'est jamais qu'une interface, et dans notre cas, une interface passant par HTTP et ses bonnes vieilles méthodes (GET, POST, PUT, etc.). De facto, il peut exister moins, autant, ou plus d'interfaces que d'applications.

Mais de quelles applications parlons-nous ? C'est là, à mon avis, que se trouve le premier élément de réponse. Vous ne pouvez pas aborder le problème des versions sans savoir dans quel contexte vous vous situez.

Ensuite, qu'est-ce qui est versionné ? Il y a l'application, l'interface, les formats de données, et les données elle-même.

Si vous croisez les types d'applications avec les types de versions, cela donne un large panel de cas à traiter, ce qui n'est pas une mince affaire. J'ai remarqué que la tentation est forte de résoudre un problème par la mauvaise solution, en se trompant simplement sur "qu'est-ce qui doit être versionné", et "de quoi dépendent les versions".

Dans cette première partie, j'aborde le cas d'une application versionnée et distribuée à des clients qui installent chacun leur propre instance, et peuvent utiliser chacun une version différente.

Le jeu de la vie

Par Florian Strzelecki - 12:22 - 05.01.2014

Tags : Python, Programmation, Bonne pratique, Technique, OOP, FP

J'aime la programmation orientée objets (OOP), tout autant que la programmation fonctionnelle (FP), chacune pour des raisons différentes. Je suis toujours un peu triste lorsqu'un développeur critique l'un en disant que l'autre est mieux parce que [inséré ici un argument basé sur une différence de fonctionnement]. L'un n'est pas mieux que l'autre, car les deux impliquent des orientations et des compromis différents.

Dans son article "Start Writing More Classes", par Armin Ronacher, j'aime beaucoup cette note de fin d'article :

Something else I want to mention: what's written above will most likely result in some sort of warmed up discussion in regards to object oriented programming versus something else. Or inheritance versus strategies. Or virtual methods versus method passing. Or whatever else hackernews finds worthy of a discussion this time around.

All of that is entirely irrelevant to the point I'm making which is that monolithic pieces of code are a bad idea.

Pourquoi ? Parce que la plupart du temps, les comparaisons que je peux lire se base sur les erreurs et toutes ces choses parfaitement stupides que les développeurs peuvent faire - et si vous faites ou avez fait de la maintenance sur de vieilles applications, vous avez déjà forcément rencontré ce genres d'abominations. Pour Armin, ce sont les fonctions monolithiques, pour d'autres ce sera autre chose.

TL; DR: Trouver une solution n'est déjà pas facile, faire en sorte qu'elle soit à la fois simple et élégante l'est donc encore moins. Le bon sens n'étant pas la norme, aucun paradigme ne vous mettra à l’abri des singes de l'Enfer qui codent avec les pieds et font absolument n'importe quoi avec des concepts qu'ils ne maîtrisent pas totalement.

Par contre, il est bel et bien nécessaire d'avoir du recul sur les outils que nous utilisons, et de comprendre pourquoi, quand, et comment nous devrions les utiliser.

Pour qui sont les API ?

Par Florian Strzelecki - 23:51 - 19.12.2012

Tags : Web, Programmation, Bonne pratique, Technique, API

Bonne question n'est-ce pas ?

Normalement, vous avez deux réactions possibles si vous n'êtes pas développeur :

Oui, mais non. Enfin "presque". C'est compliqué.

Je m'amuse souvent à me décrire comme un #apidealer, parce que c'est un domaine qui me passionne. À dire vrai, le concept de deux machines pouvant communiquer, s'échanger des informations, et ce, sans la moindre intervention humaine pour ça, j'ai toujours trouvé cela magique, et c'est toujours ce que j'ai voulu faire.

Ce qui m'amène à mon premier point : une API, c'est fait pour des machines. Ce sont des machines qui vont appeler l'API, qui vont prononcer les doux verbes HTTP servant à récupérer les données, à envoyer des commandes, à demander des traitements.

En gros, pas besoin de parler comme avec les humains. Pas non plus besoin de parler comme avec un navigateur, parce que - c'est peut-être une révélation pour vous - mais le navigateur, c'est vraiment très limité pour appeler une API (sans extension/plug-in/greffon, je veux dire).

Si nous faisons des API, c'est pour permettre d'écrire des applications qui pourront comprendre les données que nous voulons les voir traiter.

Ce qui m'amène à mon second point : une API sert aux développeurs. Ce sont là les premiers usagers des API. Déjà, parce que ce n'est pas sexy comme un beau site web ou comme une jolie petite application sur son smartphone. Moi je trouve ça sexy as hell, mais je pratique le #datalove alors bon ça compte pas. Ensuite, parce que sans le travail du développeur, l'API ne sert à rien.

Une API doit être facile d'accès pour le développeur. Faire ressortir le meilleur de lui-même et de ses capacités. En échange, le développeur doit prendre soin de l'API, la mettre en valeur, ne pas la sur-exploiter pour rien. Bref, une histoire d'amour - mais seulement entre l'API et un développeur.

Il ne reste plus qu'au développeur d'être responsable, et de fournir de bons et loyaux services aux usagers finaux des données. Oui, des données, pas de l'API. Nuance.

Pourtant, ce soir j'ai entendu à peu près cette rengaine de la part d'un homme qui, par ailleurs, a un réel soucis pour ses usagers (ce que je respecte profondément et sincèrement) :

"Pour modifier l'API il faut que ça serve aux usagers.".

Et par usagers, il entendait les usagers du service qui génère les données (ici, un réseau de transport en commun - ce qui fait un paquet de données). Il entendait les usagers finaux, les gens comme vous et moi qui utilisons un service tous les jours, de manière très concrète, dans le monde physique. Tiens, rien que ce matin j'ai pris le métro, puis le bus, et pour rentrer ce midi pareil.

Oui, mais non. L'API n'est pas faite pour les usagers. Elle permet à des développeurs de proposer des services aux usagers. C'est là la grande nuance.

Si vous ne voulez pas faciliter le travail des développeurs, alors ne fournissez pas d'API. Mais ne délivrez pas une API pour vos usagers. Ce ne sont pas les cibles d'une API. Donnez des outils aux développeurs, et eux feront les outils pour les usagers. Ou alors, faites de vrai outils pour les usagers - et les API ne sont pas des outils pour les usagers.

Une API, c'est une nouvelle histoire d'amour entre un développeur, et une machine.

PHP est cassé, et alors ?

Par Florian Strzelecki - 23:55 - 18.07.2012

Tags : Web, Programmation, Ma vie, PHP, Développement, loldev, Technique

Comme si j'en avais quelque chose à faire, aujourd'hui, que PHP soit cassé. J'ai beaucoup de souvenirs désagréables, j'ai beaucoup d’anecdotes qui prouvent, à qui souhaite l'entendre, que ce langage est une horreur sans nom, un fléau apocalyptique. Je peux, sans effort autre qu'une perte de temps, prouver et alimenter chaque attaque envers ce langage, et sa communauté. D'autant que je le connais particulièrement bien (mon compte twitter et mon adresse mail sont là pour ça).

Ah ça, si j'avais été payé à la peine et à la frustration causées par ce langage, je serais multimilliardaire. Mais ce n'est pas le cas, car, comme je l'ai constaté à de très nombreuses reprises, le nombre important de développeurs PHP permet d'en faire des développeurs low-cost. Je vous épargne les citations à ce sujet.

Continuez d'utiliser PHP, cela ne me regarde plus. J'ai choisi de travailler avec d'autres langages - principalement python il est vrai, mais pas que - et j'en suis très heureux. Je n'habite pas à Paris, je ne suis pas en télétravail, je ne travaille pas dans une petite start-up innovante, bref, je suis dans une entreprise standard, dans un contexte plutôt neutre, qu'il sera difficile de traiter de "hipster" ou de "niche".

La question pour moi n'est plus de savoir si PHP est bon ou mauvais : il est cassé, et en cela il ne répond plus à aucun de mes besoins. La simplicité d'usage ? La facilité de déploiement ? Je les ai obtenues très largement avec python, qui propose plusieurs façons d'avoir une pile web complète, performante, c'est particulièrement bluffant. Petit indice : il n'y a pas que du python dans un tel système, et il demande un peu plus de compétences que savoir utiliser FileZilla (un logiciel FTP très connu sous Windows). Je n'ai jamais dit que c'était pour le premier débutant venu, mais en même temps, ce n'est pas mon métier pour rien.

Quand j'ai une application serveur (avec une GUI web) en python, je peux lui demander son état. Je peux lui demander avec qui elle communique, et avec combien de processus elle communique. Je peux lui demander de recharger sa configuration, de modifier son comportement. Je peux gérer très finement le nombre de processus qui opèrent en parallèles, visualiser, orchestrer et administrer tout cet ensemble.

Et le tout, avec une facilité qui fait pâlir d'envie le développeur PHP que j'ai été. Qui fait pâlir d'envie n'importe quel sysadmin devant gérer une plate-forme avec plusieurs centaines de serveurs. Oui, le besoin est plus complexe, mais la complexité de la solution n'est pas proportionnelle, ni exponentielle, comme elle l'est avec PHP.

C'est là mon témoignage : faites ce que vous voulez en PHP. Je le connais trop bien, et je peux comparer, tous les jours, à quel point j'avais tort, à quel point la simplicité de son écosystème n'était qu'un mensonge.

Vous n'êtes pas convaincu ? Tant pis pour vous. Moi, je m'éclate. Rien ne pourra vous convaincre, tant que vous ne comprendrez pas les autres langages, un autre système que celui de PHP.

PHP n'est qu'un mensonge.

Mélanger redirection, SEO et Webperf...

Par Florian Strzelecki - 01:12 - 07.07.2012

Tags : Web, Bonne pratique, Optimisation, Technique, #trolldredi

Je suis toujours fasciné par la capacité de mes contemporains à chercher la moindre petite bête, le moindre petit détail, là où il n'y a pas besoin de le faire. Ou à, plus précisément, tenter de résoudre un problème qui n'existe pas.

Suite à l'article Réconcilier SEO et WebPerf au niveau des redirections de (sous-)domaines j'ai discuté rapidement sur twitter... mais manifestement mes objections n'ont pas été comprises (mais c'est difficile avec des tweets de débattre de ça).

D'ailleurs, je vous invite à lire les commentaires de l'article, car deux personnes ont déjà soulevé ce que j'ai essayé de dire. Comme le sujet m'intéresse un peu (et que nous sommes vendredi), je vous livre mes réflexions sur le sujet.

Quel est le problème ?

Première question : quel est le problème ? Non mais, vraiment, où est le soucis d'une redirection (de www vers no-www, ou no-www vers www) ?

Si votre site est bien fait - et il l'est, puisque nous sommes des experts, n'est-ce pas ? - vous n'exposez, au monde extérieur, aucun lien incorrect. Les liens de ces immondes boutons collés avec des scripts externes (et qui suivent à la trace vos utilisateurs qui n'ont rien demandé) utilisent le bon nom de domaine, tout comme vos flux rss, vos newsletters, etc.

Bref, de votre côté, tout est bon, et il n'y a donc pas lieu de craindre ni pour votre référencement, ni pour vos performances. Vous assurez grave, bravo !

Considérons ensuite vos utilisateurs, ces gens bizarroïdes qui utilisent "cmd+entrée", et tapent directement le nom de votre site dans leur barre d'adresse. Concrètement, s'ils perdent 75 millisecondes à attendre que leur navigateur trouve la bonne adresse, puis redirigent vers le bon domaine, je pense que ce n'est vraiment pas un soucis. Par contre, si la moindre petite redirection chez vous implique une seconde, je pense que vous avez de sérieux problèmes à résoudre avec votre serveur.

Quant à ceux qui n'indiquent rien (ni TLD, ni sous-domaine), les comportements varient selon les navigateurs et les configurations : avec un label bien placé sous Firefox, cela revient à cliquer sur un favoris, et sans label, à faire une recherche Google (je viens de re-tester à l'instant, histoire d'être sûr).

Dernier petit point que je souhaite soulever : quel est l'importance d'éviter une redirection totalement minoritaire, alors que nous savons tous - vous le savez n'est-ce pas ? - que les scripts des publicités, comme tous les autres chargement de ressources externes et en dehors de votre contrôle, sont bien plus à même d'effectuer des redirections qui pénaliseront le chargement de la page. Pas seulement le chargement de la première requête, mais bel et bien pendant l'ensemble du chargement de la page. C'est à dire, en même temps que le chargement de vos images, de vos scripts et feuilles de style externes. L'échec complet quoi.

Bref, un non-problème. Mais on va dire que c'est quand même un problème, rien que pour parler des solutions.

Quelle est la solution ?

L'internaute moyen ne vois pas où est le problème d'attendre 75 millisecondes de plus dans le chargement de sa page pour une seule et unique redirection. Par contre, il râle lorsque le site affiche plusieurs publicités, chacune faisant perdre plusieurs secondes à sa navigation. Je dis ça, je ne dis rien. Enfin si, je dis quelque chose, je sous-entends même un petit peu que c'est chercher à résoudre un non-problème, mais je l'ai déjà dit plus haut.

Quant aux moteurs d'indexations, ils doivent être considérés comme des utilisateurs comme les autres. Si vous voulez leur parler, vous avez les codes de réponse HTTP (2xx, 3xx, 4xx mais aussi 5xx). Vous avez aussi les robots.txt et les sitemap.xml - mais je ne vais pas vous apprendre le métier.

Si vous cherchez à les cibler plus particulièrement, vous pourrez tout aussi bien avoir de bonnes comme de mauvaises surprises. Les User-Agents n'ont rien de fiable ni de sûr, ils peuvent changer, ne pas être utilisé comme vous le pensé (je suis toujours aussi surpris du fonctionnement de certains User-Agent), et tout un tas d'autres trucs que seuls les robots qui rêvent de moutons électroniques peuvent comprendre.

Dernier point : si vous commencez à avoir des règles conditionnelles de réécriture d'URL pour ça il est peut-être temps de se demander où vous mène toute cette complexité. Oui, je sais, la simplicité, c'est un truc de hipster-bobo-indépendant-hackiviste. Mais quand même, la simplicité, c'est souvent une bonne pratique de WebPerf.

Un A dans YSlow

Il y a un soucis que je me pose avec cette recherche d'une bonne note de WebPerf. Je ne veux pas remettre en cause la note ou sa recherche. Mais par contre, quel est l'intérêt de tester des liens avec le mauvais sous-domaine ?

Parce que, concrètement, si le fonctionnement normal et principal de votre site, c'est sans le sous-domaine (ou avec, vous remplacez mentalement par l'inverse), en dehors d'une éventuelle phase de transition, vous n'avez aucune raison de tester la mauvaise version. Ou alors uniquement pour vérifier que votre serveur sait faire une redirection rapidement et efficacement.

Le reste, ça n'a pas d'importance.

On me souffle à l'oreille qu'un A dans YSlow n'est pas vraiment un bon objectif. Et je vous épargne la citation exacte pour vos chastes oreilles.

La bootstrapite aiguë

Par Florian Strzelecki - 01:15 - 02.06.2012

Tags : Framework, Technique, #trolldredi, css, lesscss, twitter bootstrap

Aujourd'hui, plutôt qu'un #loldev du jour, je préfère poster dans le #trolldredi. Et aujourd'hui, je vais troller sur Bootstrap by Twitter.

Préambule : ceci est un troll, vous avez le droit de répondre, mais n'oubliez pas que je ne me prends pas vraiment au sérieux (sauf sur certains trucs), et que, globalement, j'en ai pas grand chose à faire de bootstrap. Utilisez le si ça vous chante, ce n'est pas non plus le mal absolu.

Enfin, quoi que...

Je pense - notez l'usage du "je" d'une part, et du "pense" d'autre part - qu'il s'agit d'un fléau terrible, d'une maladie cataclysmique, d'une épidémie apocalyptique - je dis cela en toute modestie, bien évidement. De là à dire qu'il y a un lien entre #ZombieBukkake et Bootstrap il n'y a qu'un pas, mais je vous laisse le faire. Le pas, pas ZombieBukkake. Enfin, ça, ça ne me regarde pas.

D'ailleurs, si vous croisez quelqu'un dans la rue qui gémit un "boooostrappp" c'est peut-être qu'il faut l'achever à coups de pelle. Et vite, de préférence.

Bootstrap et Less

La documentation n'est pas spécialement claire sur le fonctionnement de bootstrap, mais vous risquez très vite de vous retrouver avec ceci directement en prod :

<link rel="stylesheet/less" href="/path/to/bootstrap.less">
<script src="/path/to/less.js"></script>

Enfin, ça, c'est si vous arrivez à trouver la bonne partie de la documentation (cela dit, c'est un peu plus simple en passant par le site de {less}).

Et là, j'entends déjà les prophètes noirs de ce "framework css" :

Hérétique ! C'est uniquement pour le dev, il faut compiler les fichiers .less avec lessc !

Je sais. J'ai fini par trouver les informations dont j'avais besoin pour :

  1. Comprendre de quoi il s'agit (rien que ça),
  2. installer tous les paquets, logiciels, bibliothèques, etc. requis, (j'en ai encore des cauchemars)
  3. prier 50 fois les dieux de mon choix, en sacrifiant un hamster avec des clés usb et en rebootant 3 fois mon ordinateur,
  4. obtenir les fichiers bootstrap.css et site.css dont j'avais besoin.

Si, comme moi, vous avez eu la mauvaise idée de ne pas utiliser une version minimisée des fichiers css, vous aurez droit à un fichier de 5000 lignes (mais sinon c'est super pour les mobiles).

Hérétique ! Il faut choisir uniquement les modules de bootstrap dont tu as besoins !

Oui, alors, là, je veux bien hein, mais c'est pas exactement ce que j'appelle "une documentation claire sur le sujet" que j'ai sous la main. C'est la documentation officielle (déjà, il faut comprendre où aller pour trouver toutes ces infos), et elle n'est pas claire pour deux sous.

Pour vous abreuver de variables à utiliser avec lesscss, ça, y a de quoi faire. Tout comme il y a largement de quoi faire des tableaux avec des divs et des classes aux noms génériques. Mais une putain d'explication simple, claire, et concise de comment faire son propre bootstrap propre et minimaliste, c'est la croix et la bannière !

Pourtant, sachez-le, je pense qu'il y a moyen de faire un truc "pas trop crade".

Bref, je n'ai rien contre less (enfin, si, j'ai un tas de trucs contre, mais je comprends l'intérêt de la chose, et je pense même l'utiliser), mais je résume par l'équation suivante :

bootstrap + less == grosse prise de tête.

Divite aiguë

Je pensais cette maladie oubliée, perdue, et disparue dans les tréfonds du web des années 2004-2006, lorsque CSS est devenu vraiment populaire, que Firefox sortait en version 1.0 puis 1.5 (j'ai une anecdote croustillante à vous raconter à ce sujet d'ailleurs, mais ce sera pour une autre fois), et que tout le monde s'accordait à dire "non mais, remplacer les <table> par des <div> partout en cascade, ça va pas être possible".

FAUX

Bootstrap permet aux plus neuneux d'entre nous de faire les plus ahurissantes combinaisons de cellules façon tableau 1998 à base de div. Et ouais, ces bonnes vieilles div.

Il y a même un nom pour ça : grid system et fluid grid system.

Comment dire. Je comprends parfaitement l'intérêt d'un design qui s'aligne sur une grille. Mais vous ne croyez pas qu'il y a un problème avec ce genre de code html :

<div class="row show-grid">
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
    <div class="span1">1</div>
</div>
<div class="row show-grid">
    <div class="span4">4</div>
    <div class="span4">4</div>
    <div class="span4">4</div>
</div>
<div class="row show-grid">
    <div class="span4">4</div>
    <div class="span8">8</div>
</div>
<div class="row show-grid">
    <div class="span6">6</div>
    <div class="span6">6</div>
</div>
<div class="row show-grid">
    <div class="span12">12</div>
</div>

Avec ce genre de documentation :

With the static (non-fluid) grid system in Bootstrap, nesting is easy. To nest your content, just add a new .row and set of .span* columns within an existing .span* column.

Ce qui donne ce genre de règles pour css :

.row .span6 .row .span6 { /* ... */ }

Quoi ? La lisibilité ? Pour quoi faire ? On a un putain de grid-system-de-la-mort pourquoi on se ferait chier avec de la sémantique ou bien de la lisiblité.

lol noob.

Vous en pensez ce que vous voulez : moi, je n'en pense que du mal. Je comprends l'utilisation de technologie comme less pour factoriser le calcul des tailles des blocs pour chaque élément. Mais là, c'est exactement ce qui était reproché à la divite aiguë en 2007, et qui est toujours aussi désagréable.

Surtout que - excusez du peu - mais tant qu'à mettre un framework de css aussi lourd que bootstrap, autant utiliser des balises HTML5 (genre <aside>, <footer>, et autre <article>) avec un peu de JS pour supporter les anciens navigateurs. Après tout, ce ne seront que quelques ko en plus à télécharger.

Digression sur la notion de "standard"

Deux remarques de @revolunet :

C'est surtout l'aspect standardisation du markup qui est utile. Une bonne base pour themer, partager et faire evoluer des pages web

Deux remarques en un seul tweet. Je ne vais m'intéresser qu'à la première, puisqu'elle me révulse profondément. La seconde, j'en parle dans la suite, dans la partie des trucs biens.

La standardisation, justement, à quoi sert-elle ? Dans un cas comme bootstrap, à ce que tout le monde puisse parler de la même chose en même temps. C'est bien, c'est louable, mais il y a une chose qui manque : un sens à tout ça.

Ici, la standardisation ne sert qu'elle-même, et pas à donner du sens à ce que vous écrivez. Cette "standardisation" est l'exact contraire du travail de standardisation sur HTML5 & CSS3.

J'ai hâte que nous ayons un grid-layout en CSS3 implémenté partout et utilisable, mais je ne suis pas prêt à farcir mon code html d'une multitude de classes sans aucune sémantique.

Imaginez le bordel que ça doit être quand il faudra savoir si dans ce .row là il faut mettre un .span4 + .span2 ou deux .span4. Imaginez que cette question se pose dans un flot de .row .span4 les uns à côté des autres. Avec des .container dans les coins, histoire de vous faire un croche-pied.

Bon courage, mais je ne veux pas de cette standardisation là.

Ton .css tu ne comprendras plus.

Autre point qui me frustre particulièrement avec bootstrap. Que vous ayez un .less (et ses multiples import), ou un immense .css, comment comptez-vous vous y retrouver là dedans ? Comment allez-vous vous assurer, à chaque instant, de comment ça marche tout ce bordel ?

Et si, par malheur (et ça arrive tout le temps en réalité), quelqu'un se met à modifier certains fichiers .less de bootstrap, et pas seulement le local.less... bon courage !

lol noob c'est que du css on s'en fout !

Un intégrateur débutant qui mettait des #id partout pour tout. Il est mort dans d'atroce souffrance. IE6 overdose.

Je me suis retrouvé devant ce cas concret : on me file des .less, et à moi de me débrouiller avec. Problème de compilation parce qu'il manque un truc ? Débrouille toi ! Problème parce qu'un style ne fonctionne pas / ne fait pas ce qu'il devrait ? Débrouille toi ! Et surtout, paye toi tous ces fichiers dans tous les sens.

La doc n'est d'aucun secours, puisqu'il s'agit d'un problème que vous aurez entre votre html, vos css perso, et les règles de bootstrap. Ce ne serait pas un problème si c'était juste un reset de style, ou des classes ayant un sens sémantique...

... mais là, vous avez bootstrap, et son paquet d'éléments tous plus génériques les uns que les autres.

Et en prime, dès que vous voudrez changer le style de quelque chose, vous devrez modifier les classes css que vous utilisez pour cet élément (et donc le html)... #fear.

Là où c'est utile...

Bon, je vais cesser de troller pour aujourd'hui. Il y a un tas de trucs qui me dérangent avec bootstrap de twitter, mais il y a aussi des points positifs.

Comme le disait @revolunet c'est une façon de partager un point commun dans une équipe pour partager un style. Cela permet d'avoir un rendu "basique et standardisé" rapidement. Enfin, rapidement... je me comprends.

Standardiser des techniques d'utilisation de CSS

Je sais, mon précédent point était de dire que la standardisation de bootstrap c'était le mal. Et je n'en démords pas.

Par contre, l'effort de proposer une approche de "standard" est très intéressante - principalement pour le grid-system justement. Mais il aurait été beaucoup plus intéressant d'exposer des structures à réutiliser avec less, plutôt que des classes génériques à tout faire.

Après tout, une grille n'obéit qu'à quelques règles relativement simples, principalement, des règles mathématiques (je ne vais pas détailler, c'est un peu long et technique, mais ce n'est pas aussi compliqué que ça en a l'air).

Bref : bootstrap propose un concept intéressant, en cela qu'il démontre la logique mathématique derrière un tel système.

Un exemple pour la route :

Avec bootstrap, si vous voulez mettre un style à vos tables html :

<table class="table table-striped table-bordered table-condensed">
...
</table>

Ok... mais pourquoi ne pas simplement avoir des mixin less de ce style là :

# in table.less
.table-striped {
    /* style for striped table */
}
.table-bordered(@border: 1px) {
}

# in local.less
.customer_table {
    .table-striped;
    .table-bordered;
}
.product_table {
    .table-bordered;
}

Et voilà : vous obtenez le même résultat final, mais au lieu de rajouter plusieurs classes à votre élément <table>, vous ne lui ajoutez qu'une seule classe : la votre, celle qui a du sens customer_table pour vos clients, product_table pour vos produits. Si vous devez changer le style de ces deux tables, vous n'avez pas à changer le code html.

Et puis surtout si vous changez l'un vous ne changez pas l'autre par erreur...

Prototypage et application

Pour un prototype, bootstrap est peut-être une bonne idée. Je dis bien : peut-être. Je suis convaincu que, pour un petit site, il est une usine à gaz. Mais pour un prototype, il pourrait permettre de présenter quelque chose rapidement.

De même, pour une application web de type "intranet" ou "panneau de commande" réservée à un petit groupe qui utilise un système d'administration (de contenus par exemple, ou de gestion comptable, ou de ressources quelconques), il permet de standardiser rapidement le style de l'application. Mais cela reste un cas particulier - certes très utile (ce ne serait pas un mal à mon boulot par exemple), mais pas n'importe où n'importe quand.

Trolldredi, c'est fini pour aujourd'hui.

Allez, je m'arrête là, c'est déjà beaucoup de temps passé pour comprendre, analyser, et découvrir bootstrap. C'est un outil dont je n'ai pas besoin, mais il m'aurait fait mettre un doigt dans less. Ce qui n'est pas un mal en soi.

Il y a des bonnes idées à reprendre, mais en l'état : je dis non. Filez moi la même chose avec des mixin lesscss, et peut-être que j'y prendrai goût (mais c'est pas sûr).

Tu ne routeras point comme un cochon

Par Florian Strzelecki - 19:57 - 11.04.2012

Tags : Framework, Web, Documentation, Bonne pratique, PHP, loldev, Technique

Ces derniers jours, j'ai un problème avec les URLs. Plus spécifiquement, avec certaines URLs, qui sont gérées par certains mécanismes de "routage d'url" (et de réécriture d'url).

Il y a les bons routages et les mauvais routages.

Les bons routages d'URLs considèrent un format type (à base d'expressions régulières par exemple), et permettent d'associer une URL concrète à ce format, et d'en tirer une représentation à usage interne pour l'application. Les mauvais routages d'URLs considèrent un format type (à base d'expressions régulières par exemple), et permettent d'associer une URL concrète à ce format, et d'en tirer une représentation à usage interne pour l'application. Sauf que c'est un mauvais routages d'URL.

Trêve de blagues (et faisons plutôt la paix) (...) (ok j'arrête) voici le vrai problème que j'ai en ce moment : lorsque / et /home/default produisent exactement le même résultat, c'est à dire, pointent finalement sur la même "ressource". Autant c'est bien gentil de vouloir de jolies urls, et d'avoir des mécanismes simples pour gérer automatiquement un tas de cas, mais ce n'est quand même pas très RESTFull au final (je hais la duplication de contenu).

Car il s'agit bien d'un problème de "comment", et pas de "quoi" : réécrire des URLs à usage interne, et adapter le routage en conséquence, c'est tout à fait normal - ce n'est pas sale. Mais autant le faire bien, s'il vous plait. Ce problème n'est malheureusement que rarement traité, même si lors de mes recherches, j'ai été agréable surpris par les documentations de Symfony 2 et CakePHP.

Faisons un petit tour d'horizon de ce que nous proposent certains frameworks "à la mode" du monde PHP, puisque c'est majoritairement chez eux que j'ai rencontré ce problème, que chaque développeur devrait connaître et savoir gérer. Je veux dire, en commençant par reconnaître le problème quand ils l'ont devant leurs yeux.

Zend Framework

Allez, on commence avec du lourd, du sale qui tache : ce bon vieux Zend Framework. Après toutes ces années, je ne sais pas pourquoi je dis encore "ce bon", parce que je le trouve tout sauf bon, et même si ce n'est pas le sujet, encore une fois, ce framework montre des faiblesses de conception.

Dans sa documentation concernant le système de routage d'URL, vous pouvez tomber sur Zend Framework: Default Routes, section qui vous explique ceci :

Zend_Controller_Router_Rewrite comes preconfigured with a default route, which will match URIs in the shape of controller/action.

Ah, donc non seulement c'est le routeur par défaut, mais en plus il a pile le comportement qu'on ne veut pas avoir. Heureusement qu'à la fin de cette section il est ajouté qu'on peut supprimer cette route par défaut... bien que, par expérience, les résultats sont plutôt hasardeux (du genre vraiment, et pénible avec ça).

Cela dit, ce n'est pas encore terminé, sur la partie Dispatcher vous pouvez lire ceci :

If any of the module, controller, or action are not found, it will use default values for them.

Et pour revenir sur mes expériences : oui, ça peut devenir un problème, puisque le mécanisme qui utilise les routes d'URLs a déjà sa façon de voir les choses, et ce n'est pas toujours à votre avantage. Rien que d'y penser... non, je n'ai pas envie d'y penser.

CakePHP : presque !

Je ne connais pas bien CakePHP, et de ce que j'en ai lu, ce n'est pas trop mal - mais bon, mon avis ne vaut pas grand chose, testez plutôt vous-même. Que nous dit la documentation de CakePHP : Default Routing ?

You can access an action directly via the URL by putting its name in the request.

Brrr... ok, la même chose que pour Zend-Framework (mais avec une documentation plus sympa cela dit), ce qui n'est pas vraiment une bonne chose. D'autant que la suite n'est pas mieux, puisqu'on retrouve même des conseils sur comment faire de mauvaises choses automatiquement :

The keys of the array should be named after the route elements in the URL, or the default elements: :controller, :action, and :plugin. The values in the array are the default values for those keys.

Erm... je reviens, je vais boire une litre ou deux de ice-tea, pour me calmer. Ah moins que... CakePHP : Disabling the default routes : en voilà deux paragraphes intéressants !

Dommage qu'ils ne prennent pas beaucoup de place, mais ils ont le mérite d'être là. Je retiens plus particulièrement ceci, qu'il faudrait peut-être mettre en gras, en gros, avec des néons et des balises <blink></blink> tout autour (non, je déconne, les néons c'est en trop) :

If you have fully customized all your routes, and want to avoid any possible duplicate content penalties from search engines [...]

Bon, d'accord, la raison mise en avant, c'est pour faire plaisir aux moteurs de recherches : en attendant, c'est déjà une mise en garde sur les dangers d'un routage un peu trop permissif et automatique. Un bon point, au moins (et puis la doc a vraiment une jolie tête).

Symfony : le 1, et puis le 2 ensuite

J'ai connu une version de la branche 1.x de Symfony, et si le framework ne m'a pas marqué plus que ça, je me souviens très bien de son système de routes, qui est relativement facile à configurer (notez que je ne parle jamais de performance ici). Cette fois je peux nuancer plus facilement mon discours.

Tout d'abord, il est parfaitement possible de se passer d'une route par défaut avec Symfony 1.x, et je le conseille même vivement. Le soucis, c'est que la documentation section 9 ne précise pas forcément très bien cet aspect là. Je reprends cet exemple de code fourni :

# generic rules
# please, remove them by adding more specific rules
default_index:
  url:   /:module
  param: { action: index }
default:
   url:   /:module/:action/*

Alors, oui, il y a un commentaire, mais c'est à peu près la seule remarque sur le sujet, et j'ai vu bien des développeurs l'ignorer complètement, l'oubliant, et reléguant cette ligne au fin fond des poubelles de l'histoire de leur framework favori.

D'ailleurs, un exemple de code un peu plus loin ne reprend déjà plus le commentaire, c'est dire... bref, là encore, Symfony rejoint la liste des frameworks qui permettent des choses bien crades, et qui en plus ne documentent pas bien la chose. Pas cool.

Symfony 2.x : de bonnes inspirations ?

N'ayant pas travaillé avec Symfony 2, j'ai du faire un peu plus de recherches, et j'ai été agréablement surpris par une documentation bien plus claire, et plus encore, par le système de routage.

Ce derniers ne propose pas (de manière documentée explicitement en tout cas), un mécanisme "générique" pour des urls qui peuvent correspondre automatiquement à une représentation interne (du genre :controller/:action comme dans la version 1.x). En cherchant un peu, j'ai trouvé que c'était tout à fait possible de mal faire les choses quand même, mais cela me semble moins grave, du fait d'une information qui n'est pas mise en avant du tout (là encore, contrairement à la version précédente).

Bref, bon point pour Symfony 2.x, qui propose un mécanisme de routage d'URLs relativement intelligent. Le point bonus : il propose même un système d’annotations, comme les décorateurs en Python, pour gérer ses URLs... un peu à la manière de Pyramid (un framework web minimaliste en python).

TL;DR: Tu ne routeras point comme un cochon

Vous l'aurez compris aux travers de ces exemples, je ne critique pas le principe de routage d'URLs en lui-même, mais seulement son implémentation, sa documentation, et les conseils fournis aux développeurs. Il y a dans la conception de ces outils une mauvaise compréhension des contraintes posées par les URLs et les bonnes pratiques. Les solutions apportées, bien que facile d'utilisation (et encore...), ne me semblent pas bien répondre aux problématiques posées, ou alors, seulement en partie, alors que toutes les parties sont importantes.

Je compare ces problèmes de conceptions à d'autres implémentations : celles de Django et de Pyramid, deux frameworks web en python. Ni l'un, ni l'autre, ne permettent simplement d'instaurer un tel mécanisme, mais proposent à la place une grande souplesse et une grande réutilisabilité.

Comme quoi, c'est un problème de conception, pas de fonctionnalité.

Sinon, une dernière blague pour la route ? Essayons de ne pas nous fâcher en si bon chemin !

UnicodeEncodeError : fichiers, formulaires, django et gunicorn

Par Florian Strzelecki - 17:17 - 09.03.2012

Tags : Django, Python, Programmation, Développement, Charset, Problème, Technique

L'erreur bête et très frustrante du jour vient d'un petit formulaire qui permet de téléverser un fichier quelconque sur le serveur. Vous me direz, c'est la base du formulaire web avec des fichiers, et il ne devrait pas y avoir de soucis particuliers... sauf lorsque lesdits fichiers ont des accents dans le nom de fichier, et qu'une erreur aussi frustrante que difficile à analyser débarque avec ses gros sabots.

Alors, premier réflexe : chercher sur le net, entre Stack Overflow et le tracker de bug de django. Vous pourrez tomber sur ce genre de discussions : UnicodeEncodeError: 'ascii' codec can't encode character.

Cela donne déjà un premier aperçu d'où vient le problème, à savoir, une configuration de locale sur l'environnement serveur. Pour vous en dire plus, voici les symptômes auxquels j'ai été moi-même confrontés :

La première "solution" qui est proposée, c'est que l'environnement serveur soit correctement configuré, avec, entre autre, une modification du fichier /etc/apache2/envvars. Personnellement, j'ai essayé, mais cela ne fonctionne pas.

Bref, que faire dans ces cas là ?

Comprendre et analyser le problème

Je ne suis pas un expert, mais voici ma démarche pour comprendre puis résoudre ce problème.

Tout d'abords, j'ai regardé la locale de la machine à l'aide de la commande locale, et j'ai obtenu ceci :

LANG=fr_FR.UTF-8
LANGUAGE=
LC_CTYPE="fr_FR.UTF-8"
LC_NUMERIC="fr_FR.UTF-8"
LC_TIME="fr_FR.UTF-8"
LC_COLLATE="fr_FR.UTF-8"
LC_MONETARY="fr_FR.UTF-8"
LC_MESSAGES="fr_FR.UTF-8"
LC_PAPER="fr_FR.UTF-8"
LC_NAME="fr_FR.UTF-8"
LC_ADDRESS="fr_FR.UTF-8"
LC_TELEPHONE="fr_FR.UTF-8"
LC_MEASUREMENT="fr_FR.UTF-8"
LC_IDENTIFICATION="fr_FR.UTF-8"
LC_ALL=

Ensuite, j'ai lancé l'interpréteur python, pour en savoir plus :

>>> import locale
>>> locale.getlocale()
(None, None)
>>> locale.getdefaultlocale()
('fr_FR', 'UTF-8')

Bon, je remercie ici mYk et linova sur IRC de m'avoir fait chercher dans ces directions, et de m'avoir proposé ensuite de vérifier la valeur de ces informations là dans le contexte de mon application django. J'ai donc affiché ces valeurs dans un template et voici les valeurs que j'ai obtenues avec le serveur de dev de django :

loc: (None, None)
loc_def: ('fr_FR', 'UTF-8')

Puis la même chose, mais avec gunicorn :

loc: (None, None)
loc_def: (None, None)

À partir de là, j'ai pu enfin comprendre le problème, et comment le résoudre. Merci à linova pour l'idée sur IRC.

Merci encore une fois au chan #djangocong sur freenode !

Résoudre le problème

J'utilise runnit pour gérer mes différentes applications servies par gunicorn, et j'ai donc des petits scripts (comme pour init.d), dans lesquels j'ai ajouté ceci :

export LC_ALL=fr_FR.UTF8

Et c'est tout. Oui, tout à fait, c'était juste ça : la locale n'était pas définie dans ce contexte très particulier, et pas dans un autre, ce qui explique tout mon problème avec ces foutus fichiers. J'ai redémarré les différentes applications, et je n'ai plus constaté de problèmes.

Alors, si vous aussi vous avez ce problème, je vous conseille de suivre la même démarche que moi : vérifiez vos locales, vérifiez vos locales via l'interpréteur python d'une part, puis dans le contexte de votre application plus spécifiquement.

Enfin, allez voir du côté du script de votre serveur, et voyez s'il n'y a pas quelque chose à faire avec.

Python-epub : un projet sous licence libre pour le format epub

Par Florian Strzelecki - 18:05 - 08.03.2012

Tags : Python, Programmation, Format ouvert, Développement, Livre numérique, epub, Technique

Ce n'est pas sans une certain appréhension que je publie ce billet, où je souhaite vous parler de mon travail actuel : la bibliothèque python-epub, placée sous licence libre (LGPL), et disponible officiellement sur pypi.

De l'appréhension, car je n'ai pas la prétention d'être un maître dans l'art du code python d'une part, pas plus qu'être le plus grand expert du format epub d'autre part. Et puis, surtout, c'est la première fois que je publie vraiment du code sous licence libre !

Mais, passons mes états d'âme pour aller au cœur du sujet.

Téléchargement et installation

Tout d'abords, si vous cherchez directement comment l'installer, et avoir une documentation technique, vous pouvez aller sur le site officiel du projet : http://epub.exirel.me. Il est à jour et documente la dernière version publiée (et pas la version en cours de développement).

Pour faire simple : utilisez pip install epub. Si vous voulez la version de développement (je ne garantis pas la stabilité dudit code), vous pouvez faire un clone des sources disponibles en ligne : https://bitbucket.org/exirel/epub.

À quoi ça sert ?

En premier lieu, à ouvrir des fichiers au format epub, dans la version 2 de la spécification (la 3 restant encore trop jeune et trop peu utilisée pour le moment). Qu'est-ce que le format epub ? Il s'agit d'un format ouvert de livre numérique. Il est utilisé de manière majoritaire dans l'édition numérique, et peut être lu par des applications sur mobiles et tablettes, et surtout, sur des liseuses à écran à encre numérique (de type Kobo ou Cybook Odyssey). Je n'entre pas ici dans la polémique autour des autres formats, ce n'est pas le sujet (mais vous pouvez toujours me poser des questions par email, ou via twitter et g+).

Le format Epub 2

Le principe d'un fichier epub, c'est d'avoir un fichier compressé au format Zip, qui contient plusieurs fichiers importants :

Les autres fichiers composent le "vrai" contenu du fichier epub et sont référencés de plusieurs façons, tant par le fichier OPF qui en fait une liste exhaustive, que par le fichier de navigation NCX qui propose une navigation pour l'utilisateur (comme une table des matières, des illustrations, etc.).

Le code python

De son côté, la bibliothèque python-epub permet de représenter les données d'un fichier epub : fichier OPF, fichier NCX, et le contenu du fichier. Elle est même découpée en trois parties :

Ce découpage logique permet une grande souplesse dans l'utilisation de cette bibliothèque. De prime abord, elle ne permet (pas encore) de générer un fichier epub, mais certains éléments sont déjà manipulables en dehors d'un fichier epub pur : c'est le cas pour le format OPF et le format NCX, dont les modules dédiés permettent d'une part de lire ces formats, mais aussi de générer des fichiers dans ces formats.

Le projet

Licence libre : LGPL

Le projet est placé sous la licence libre LGPL : elle correspond à ma volonté, en tant qu'auteur, de rendre libre le fruit de mon travail pour les utilisateurs. Si d'autres développeurs souhaitent utiliser mon travail (avec ou sans modification) ils n'auront pour seule obligation que de donner les mêmes droits sur cette partie là de leur travail (par exemple, dans le cas d'une application, seule ma bibliothèque devra être sous une licence libre compatible, pas l'ensemble de l'application).

Je pense d'ailleurs que c'est la seule chose pour laquelle je n'envisage pas de changement, contrairement au code en lui-même.

Les évolutions à apporter et celles envisagées

Pour le reste – le code concret, mais aussi la documentation – j'envisage beaucoup d'évolutions, et certaines dont je ne suis pas encore sûr de la façon de les implémenter. Tout d'abords, pouvoir créer et modifier des fichiers epub : il est plutôt simple de lire un fichier epub, mais l'écriture pose beaucoup d'autres questions.

En vrac, j'ai listé les améliorations/évolutions suivantes :

Il y a encore beaucoup à faire pour que je sois pleinement fier de ce projet, mais j'avance correctement, je fais attention à la qualité du code, aux tests unitaires, et à la documentation. Je pense donc que ça va dans le bon sens.

Appel à contribution

Pour le moment, je suis le seul développeur sur ce projet, que j'ai initié en début d'année, mais si j'ai choisi une licence libre, c'est bien pour laisser la place à d'autres personnes.

Ce que je cherche ? Les bonnes volontés : contributions au code, des retours d'expérience, des demandes d'ajouts de fonctionnalités, des rapports de bugs, pourquoi pas des traductions de la documentation ?

Comme je le disais en début de billet : je ne suis pas un maître pythoniste, et au vu de mon expérience, je ne doute pas un instant qu'il me reste beaucoup à apprendre. Je prends donc toutes les remarques et critiques constructives, les avis, les optimisations, les conseils... tout !

Migration vers Django 1.3

Par Florian Strzelecki - 19:41 - 07.03.2012

Tags : Django, Python, Web, Programmation, Développement, Optimisation, Technique

Sur ma liste des choses à faire, il y avait cette migration concrète de mon site&blog vers Django 1.3. Il faut savoir que j'ai développé à l'origine avec la version 1.1, en ajoutant à l'occasion quelques petites choses de Django 1.2 lorsque j'ai mis à jour mon serveur.

Aujourd'hui, mes dernières applications sont développées avec Django 1.3, et il y a quelques petites choses en plus que j'apprécie bien. Je vous propose donc un retour d'expérience, avec un tour d'horizon de ce que j'ai modifié et eu à modifier.

Nommer les urls

Je n'avais pas touché à mon fichier urls.py depuis longtemps, et j'ai donc commencé par là : remettre certains trucs à plat, et rendre le tout plus propre et plus facile à maintenir.

Premier truc : utiliser la fonction url, qui permet entre autre chose de nommer ses urls, ce qui est très pratiques pour la suite, comme, par exemple, ne pas s'embêter à écrire des urls en dur dans les templates.

Exemple de template (avec Django 1.3) :

{% load url from future %}
<p>Lire l'article : <a href="{% url 'entry' entry.url %}">{{entry.title}}</a></p>

Vous noterez l'usage de {% load url from future %}, car le template-tag url a un comportement modifié pour les futures version de Django.

Du côté de la view, cela permet aussi de se simplifier la vie :

def redirect_to_tag(request, section_url):
    """Section now redirect to tag."""
    return redirect('tag', permanent=True, tag_url=section_url)

J'ai simplement nommé l'une de mes urls "tag", et la fonction redirect s'occupe du reste. Si je change la forme des url des tags, je n'aurai pas à changer le code de cette view.

Séparer media et static

Apparu avec Django 1.3, la gestion des fichiers "statiques" a été grandement amélioré par l'intégration de django-staticfiles directement dans le projet Django en tant que django.contrib.staticfiles.

J'ai pu découvrir son fonctionnement avec mon dernier projet (un truc secret pour le moment...), et c'est vraiment très pratique. Le principe ? Au lieu d'avoir un répertoire de médias, intégrant les fichiers de l'application (css, js, etc.) et les fichiers téléversés par les utilisateurs, il y a maintenant deux espaces différents.

Je ne vais pas détailler ici tout ce que j'ai eu à faire mais voici un rapide résumé :

Ensuite, pour lancer la collecte, la commande suivante fonctionne très bien :

python manage.py collectstatic

Au passage, j'ai aussi simplifié un peu ma configuration Apache, mais ça, c'est un autre sujet.

Et d'autres petites choses...

S'il n'y avait que ça, ce serait trop facile. Et bien, en réalité : il n'y avait que ça.

J'avais déjà fait une première passe pour simplifier grandement le code, faire le ménage, et retirer ce qui n'était pas bien fait et/ou bien pensé (avec un code qui a presque deux ans, c'est normal, j'ai beaucoup appris pendant ce temps). Du coup, une migration assez simple, avec, au déploiement, pas un seul soucis.

J'ajoute, au passage, que c'est une bonne expérience, qui permet de revenir sur ce qui a été fait avant, et sur ce que je fais aujourd'hui - et donc, de pouvoir mesurer l'écart.

DjangoCong 2012 à Montpellier, en face de la mer !

Par Florian Strzelecki - 17:14 - 29.11.2011

Tags : Django, Python, Framework, Web, J'aime, Djangocong, Technique

J'étais à la DjangoCong 2011, c'était à Marseille, et c'était super. Et ça tombe bien, parce que pour l'édition 2012, j'assiste les organisateurs dans cet évènement, qui a été pris en main par une équipe locale... à Montpellier !

Plus exactement à Carnon-Montpellier, devant une immense étendue de sable chaud et la mer Méditerranée.

Quand ? Le 14 et 15 Avril 2012. Prévoyez vos billets de train !

Où ? À Carnon-Montpellier, dans le Sud de la France. Il y a une gare TGV et tout le confort sur place (la mer, et on l'espère tous du Soleil).

Le lieu ? Dans la journée, ce sera à la Maison Familiale EAGA, et le soir au Gédéon. Oui, ça vend du rêve en barre (j'ai hâte).

Les inscriptions ne sont pas encore ouvertes : elles le seront lorsque l'appel à conférences sera clôturé, c'est à dire aux environs du 13 - 15 Janvier 2012. Donc, si tu as quelque chose à proposer, il est temps de le faire !

La mer devant Carnon-Montpellier

Image : La mer devant Carnon-Montpellier - DjangoCong - DR

Multi-Db avec Zend

Par Florian Strzelecki - 20:20 - 07.10.2011

Tags : loldev, Framework, Web, Zend Framework, Technique, Documentation, Programmation, SQL, PHP

Depuis que je travaille avec le Zend Framework (et ce n'est définitivement pas par passion ni envie), je ne passe pas une semaine sans avoir besoin d'aller voir dans le code source du framework pour comprendre ce qu'il fait, pourquoi, comment, et en quel honneur.

Et généralement, je me marre - enfin pas vraiment, mais faites comme si.

Cette semaine pour le #loldev du vendredi, c'est la documentation qui m'a donné l'info qui me manquait pour résoudre un problème qui n'est pas trivial à l'origine, mais qui devrait l'être avec un framework web digne de ce nom : comment gérer une application qui doit se connecter à différentes bases de données ?

En voilà une question intéressante... voici ma réponse.

Pages and pages of source code.

Image : Pages and pages of source code. - Neil Crosby (http://www.flickr.com/photos/thevoicewithin/) - Creative-Common By-NC-SA