• I've been working for a few months with Lift, a really nice web framework. Lift takes ideas and concepts from a few other frameworks. The main difference between Lift and other frameworks is that Lift does not enforce MVC (even though you can do MVC if you want to). Lift's approach is view-first, which means you first create the view, then you tell which part of the code will take care of dynamism.

    For more propaganda: Seven Things and Web Framework Manifesto

    I'll explain how to build REST-y URLs in a type-safe manner, with the SiteMap. The goal is to access a blog post with this kind of URL: /posts/{id}

    I'll illustrate this article with something tremendously original: a Blog, with posts. I'll use mongoDB as a backend, but it's not really important.

    The model

    It's a simple blog post. Let's just assume the author is stored in plain text.

    class BlogPost private() extends MongoRecord with ObjectIdPk[BlogPost] {
      def meta = BlogPost
      object author extends StringField(this, 32)
      object date extends DateField(this)
      object content extends TextareaField(this, 500)

      val url =// We'll see that later
    }

    object BlogPost extends BlogPost with MongoMetaRecord[BlogPost] {
      val entry =// We'll see that later
      def calcHref(bp: BlogPost) =// We'll see that later
    }
    [code=scala]
    class BlogPost private() extends MongoRecord with ObjectIdPk[BlogPost] {
      def meta = BlogPost
      object author extends StringField(this, 32)
      object date extends DateField(this)
      object content extends TextareaField(this, 500)

      val url = … // We'll see that later
    }

    object BlogPost extends BlogPost with MongoMetaRecord[BlogPost] {
      val entry = … // We'll see that later
      def calcHref(bp: BlogPost) = … // We'll see that later
    }
    [/code]

    The template

    Nothing special there. Just save it in webapp/posts.html. We have a h1 for the title, a p for some info, and a p for the actual post.

    <div class="lift:surround?with=default;at=content">
    <div class="lift:BlogPostSnippet">
    <h1 class="title"></h1>
    <p class="info"></p>
    <p class="blogpost"></p>
    </div>
    </div>
    [code=html]
    <div class="lift:surround?with=default;at=content">
    <div class="lift:BlogPostSnippet">
    <h1 class="title"></h1>
    <p class="info"></p>
    <p class="blogpost"></p>
    </div>
    </div>
    [/code]

    The snippet

    This class will be instantiated with the blog post we want to display. We just inject data in the markup.

    class BlogPostSnippet(val blogPost: BlogPost) {
      def render = ".title *" #> blogPost.title.is &
        ".info *" #> ("By " + blogPost.author.is) &
        ".blogpost *" #> blogPost.content.is
    }
    [code=scala]
    class BlogPostSnippet(val blogPost: BlogPost) {
      def render = ".title *" #> blogPost.title.is &
        ".info *" #> ("By " + blogPost.author.is) &
        ".blogpost *" #> blogPost.content.is
    }
    [/code]

    The SiteMap

    So, back to BlogPost.entry, where the magic happens. We define an entry of the sitemap which is associated with the BlogPost class. The two first arguments are the id and the displayed name of the link. The third argument is a function which takes a String and returns a Box[BlogPost]. This tells Lift how to instantiate BlogPostSnippet with the right blog post. The fourth argument is used to create an url from a given blog post. Finally, we specify the address and tell Lift we don't want this entry to show up in the menus.

    The calcHref method is just a shortcut.

    class BlogPost … {
      /* Snip */
      val url = BlogPost.calcHref(this)
    }

    object BlogPost … {
      val entry = Menu.param[BlogPost](
        "blogpost",
        S ? "blogpost.view",
        (id: String) => BlogPost.find(id),
        (bp: BlogPost) => bp.id.is
      ) / "posts" / * >> Hidden

      def calcHref(bp: BlogPost) = entry.toLoc.calcHref(bp)
    }
    [code=scala]
    class BlogPost … {
      /* Snip */
      val url = BlogPost.calcHref(this)
    }

    object BlogPost … {
      val entry = Menu.param[BlogPost](
        "blogpost",
        S ? "blogpost.view",
        (id: String) => BlogPost.find(id),
        (bp: BlogPost) => bp.id.is
      ) / "posts" / * >> Hidden

      def calcHref(bp: BlogPost) = entry.toLoc.calcHref(bp)
    }
    [/code]

    Don't forget to add BlogPost.entry to the SiteMap

    val entries = /* other entries */ :: BlogPost.entry :: Nil
    LiftRules.setSiteMap(SiteMap(entries: _*))
    [code=scala]
    val entries = /* other entries */ :: BlogPost.entry :: Nil
    LiftRules.setSiteMap(SiteMap(entries: _*))
    [/code]


    Next time I'll show you how to display comments after the blog post, while keeping the HTML structure of every comment in the template :)


  • Dans le cadre de mon stage, je bosse sur un site codé pour PHP 5.3, et j'ai donc eu à installer PHP5.3 sur ma bécane.

    Cette version de PHP n'étant pas encore dans les dépôts, et les paquets disponibles ayant des problèmes de dépendances non résolues, le plus simple c'est de compiler. Le problème c'est qu'on perd la souplesse de la gestion modulaire de PHP.

    Ce que j'ai donc fait, c'est installer PHP5.3 en tant que binaire CGI, sans toucher à ma version de PHP 5.2 installée en tant que module apache.

    Warning : Je mets cette méthode à titre expérimental, pour faire du dev et du test. Ne vous amusez pas à mettre ça sur un serveur de prod sans plus d'infos. Il y a pas mal de différences avec le fonctionnement sous forme de module.

    Lire la suite...


  • Vous l'attendiez depuis longtemps, la Version 1.5 d'Eklablog est enfin dispo.

    Au menu, beaucoup de choses codées cet été, dont quelques-unes qui me tiennent beaucoup à cœur : les galeries Flickr, le nouveau système d'ajout de media dans les articles, plus les sondages et plein de petites améliorations.
    La navigation full AJAX a sauté, c'était pas si avantageux que ça, et pas très ergnomique (les boutons précédent / suivant de votre navigateur préféré reviennent en grâce).

    Ce qui me fait le plus plaisir, c'est de voir implémenté tout mon boulot de cet été, je suis vraiment content de le voir tourner en prod sur eklablog. J'espère que ça vous sera tout aussi utile qu'à moi.

    J'en profite pour vous faire découvrir la nouvelle rubrique "Photo" de ce blog, qui intègre mes derniers chargements sur Flickr.
    La V1.5, c'est aussi la bêta de la V2, quelques privilégiés vont donc pouvoir tester en avant première le système de Designs Perso (dont moi, je suis en train de refaire le design de ce modeste blog)


    Voilà, vous pouvez tous remercier Godefroy pour tout le travail qu'il a fourni, je suis encore bluffé par le système des Designs (et pourtant j'en entends parler depuis longtemps).
    Enjoy, et n'hésitez pas à rapporter tout bug* que vous pourriez débusquer.

    Le feedback, c'est bon, mangez-en ! (Enfin bref, je me comprends)

    * It's not a bug, it's a random feature

  • Je viens de faire un benchmark entre JSON et serialize en PHP, et apparemment JSON est vraiment plus rapide.
    J'ai fait un tableau contenant 500 sous tableaux, puis j'ai testé la vitesse de serialize, json_encode, unserialize et json_decode. Par JSON, la linéarisation et la délinéarisation sont beaucoup plus rapides (2 secondes de moins sur 1000 itérations, c'est quand même pas mal)
    En plus, un tableau linéarisé à la JSON prend un peu moins de place, ce qui n'est pas plus mal.

    $tableau = array('Chaine',
    'cle' => array('sous tableau', 54)
    );
    echo serialize($tableau);
    //Affiche a:2:{i:0;s:6:"Chaine";s:3:"cle";a:2:{i:0;s:12:"sous tableau";i:1;i:54;}}
    echo json_encode($tableau);
    //Affiche {"0":"Chaine","cle":["sous tableau",54]}
    [code=php]$tableau = array('Chaine',
    'cle' => array('sous tableau', 54)
    );
    echo serialize($tableau);
    //Affiche a:2:{i:0;s:6:"Chaine";s:3:"cle";a:2:{i:0;s:12:"sous tableau";i:1;i:54;}}
    echo json_encode($tableau);
    //Affiche {"0":"Chaine","cle":["sous tableau",54]}
    [/code]



  • J'ai été gentiment obligé^Wconvié par Skreo à décrire mes petites habitudes en programmation...
    Je m'y attelle donc tout de suite.

    Les noms de variables, fonctions, méthodes

    Déjà, j'utilise l'anglais pour nommer mes variables. Pour la capitalisation, tout dépend de si je code Objet ou pas. Si je ne code pas objet, je fais ça à la PHP c'est à dire des mots séparés par des underscore _. Quand je code Objet, j'utilise une CamelCase bidouillée, je mets des majuscules au début de tous les mots, sauf des verbes, ce qui revient très souvent à du lowerCamelCase.

    Indentation

    Indentation de 4, j'indente tout le bloc. Pour le xHTML (je sais, ce n'est pas de la programmation), j'indente le contenu des balises de type bloc (sauf h[1-6]), et je vais à la ligne. Je vais à la ligne après un <br />, sauf s'il y en a plusieurs à la suite (mais c'est plutôt rare que ça arrive)

    Accolades

    Accolade ouvrante à la fin de la ligne de définition de la boucle. Accolade fermante sur une nouvelle ligne, au même niveau que la définition de la boucle.
    Pas d'accolades pour un bloc mono-ligne. (par exemple un if suivi d'une seule instruction).

    Espaces

    Pour les virgules et les point-virgules : pas d'espace avant, un espace après.
    Pas d'espace entre les noms de fonction et la parenthèse ouvrante. Idem pour les boucles.
    Pas d'espace entre la parenthèse fermante et l'accolade ouvrante dans les boucles.
    Pas d'espace entre le else et le if dans les else if.
    Un espace de part et d'autre des opérateurs de modification (=, +=, *=, ...).
    Un espace de part et d'autre pour les opérateurs de comparaison (==, <=, >=) et pour les opérateurs de modification dans les boucles (for, while, if), sauf pour les longues expressions.

    Guillemets

    En php, j'utilise uniquement les guillemets simples (apostrophe), sauf pour afficher des caractères spéciaux (\r, \n ...).
    En JS, ça dépend du contenu de la chaîne. Si c'est du texte tout bête, des guillemets doubles, si ça contient du HTML, je mets des guillemets simples
    Dans les autres langages, des guillemets doubles.

    Commentaires

    Dans les fichiers lambda, des commentaires pour expliquer la fonction d'un bloc, en particulier si c'est tricky
    Dans les fichiers qui définissent les classes, quelques lignes au début, puis un bloc de commentaire avant chaque méthode : utilité, variables attendues (rôle, type, valeur par défaut), valeur de retour, puis éventuellement des commentaires monolignes pour expliquer la fonction de tel ou tel bloc. Les commentaires monolignes pour les blocs sont mis après l'accolade ouvrante, précédés d'un espace.

    <?php
    //Code non_objet
    $premiere_variable = 'Texte';
    function get_property(){ // Cette fonction retourne un truc
        $retour = 'Un truc';
        return $retour;
    }
    //Code objet
    class ClasseBidon{
    /*
    Fonction sayHello
    Sert à dire Hello World
    Arguments : Aucun
    Retourne : string
    */

        public static function sayHello(){
            return 'Hello World !'
        }
    }
    ?>
    [code=php]<?php
    //Code non_objet
    $premiere_variable = 'Texte';
    function get_property(){ // Cette fonction retourne un truc
        $retour = 'Un truc';
        return $retour;
    }
    //Code objet
    class ClasseBidon{
    /*
    Fonction sayHello
    Sert à dire Hello World
    Arguments : Aucun
    Retourne : string
    */
        public static function sayHello(){
            return 'Hello World !'
        }
    }
    ?>[/code]

    Je fais suivre la chaîne à Xipoons, puis à ceux qui sont motivés :p





    Suivre le flux RSS des articles de cette rubrique