2010-09-25 4 views
4

Hunchentoot/cl-qui page CompositionHunchentoot/cl-qui composition Page

Je suis en train de mettre en place quelques pages dans hunchentoot comme une expérience, et je suis en cours d'exécution dans un mur inattendu. À titre d'exemple, j'ai la macro de modèle suivante.

(defmacro page-template ((&key title) &body body) 
    `(with-html-output-to-string 
    (*standard-output* nil :prologue t :indent t) 
    (:html :xmlns "http://www.w3.org/1999/xhtml" :xml\:lang "en" :lang "en" 
      (:head (:meta :http-equiv "Content-Type" :content "text/html;charset=utf-8") 
        (:title ,(format nil "[email protected][~A - ~]Test Site" title))) 
      (:body ,@body))))

Maintenant, quand j'ai une page de texte pur, ou un rempli de html littéraux comme

(define-easy-handler (test-page :uri "/")() 
    (page-template (:title "Splash Page") (:p "Testing testing")))

tout est OK. La page sort correctement et je peux voir les efforts de mon code instantanément. Cependant, quand j'ai une page composée d'éléments redondants, ce n'est pas aussi simple. Par exemple, disons que j'ai une page sur laquelle, pour une raison ou une autre, je veux afficher trois fils de nouvelles RSS. Ceci est un élément suffisamment complexe que je veux abstraire, donc à mon minnd, je devrais être capable de faire quelque chose comme

(define-easy-handler (test-feed :uri "/feeds")() 
    (page-template (:title "Splash Page") 
       (publish-newsfeed "http://nf-one.html") 
       (publish-newsfeed "http://nf-two.html") 
       (publish-newsfeed "http://nf-three.html"))) 


(defmacro publish-newsfeed (url &optional (item-limit 5)) 
    (flet ((get-text (s-tree node-path) (car (last (xmls-tools:find-subtree s-tree node-path))))) 
    (let ((rss-feed (xmls:parse (drakma:http-request url)))) 
     `(:div :class "rss-feed" 
       (:a :href ,(get-text rss-feed '("channel" "link")) :target "_top" (:h1 ,(get-text rss-feed '("channel" "title")))) 
       (:ul ,@(mapcar #'(lambda (item) 
           `(:li (:a :href ,(get-text item '("link")) :target "_top" (:h2 ,(get-text item '("title")))) 
             (:p :class "date" ,(get-text item '("pubDate"))) 
             (:p ,(get-text item '("description"))))) 
          (let ((items (xmls-tools:find-all-children (xmls-tools:find-subtree rss-feed '("channel")) "item"))) 
           (if (> (length items) item-limit) (subseq items 0 item-limit) items))))))))

Mais le résultat de ce qui précède est une page « Erreur serveur ». Je ne suis pas sûr de savoir pourquoi; page-template est une macro de sorte que les appels à publish-newsfeed ne doivent pas être développés jusqu'à ce qu'ils soient dans le contexte de with-html-output-to-string. Quelqu'un peut-il me dire ce que je fais mal?

De plus, en examinant de plus près les divers tutoriels Hunchentoot/cl-who, aucun d'entre eux ne semble faire ce genre de composition de page. Quelqu'un avec une expérience Hunchentoot peut-il me dire quelle est la manière correcte/canonique de décomposer une page en composants?


EDIT:

Réponse correcte par Ramarren ci-dessous; les macros with-html-output fonctionnent selon différentes règles d'évaluation. La version de publication-fil d'actualité qui fait travailler dans cette situation est en fait

(defun publish-newsfeed (url &optional (item-limit 5)) 
    (flet ((get-text (s-tree node-path) (car (last (xmls-tools:find-subtree s-tree node-path))))) 
    (let* ((rss-feed (xmls:parse (drakma:http-request url))) 
      (items (xmls-tools:find-all-children (xmls-tools:find-subtree rss-feed '("channel")) "item")) 
      (ltd-items (if (> (length items) item-limit) (subseq items 0 item-limit) items))) 
     (with-html-output 
     (*standard-output* nil :indent t) 
     (:div :class "rss-feed" 
      (:a :href (get-text rss-feed '("channel" "link")) :target "_top" (:h1 (str (get-text rss-feed '("channel" "title"))))) 
      (:ul (dolist (item ltd-items) 
        (htm (:li (:h2 (:a :href (get-text item '("link")) :target "_top" (str (get-text item '("title"))))) 
           (:p :class "date" (str (get-text item '("pubDate")))) 
           (:p (str (get-text item '("description")))))))))))))

Notez la suppression de mapcar pour dolist (je suis un Schemer, ne me donne pas trop de mal à propos goût lambdas , mais ils n'étaient pas le bon choix ici), et l'utilisation de htm pour échapper des blocs de html s-exps (h-exps?) qui ne seraient pas autrement dans le contexte de with-html-output. Enfin, j'ai dû envelopper le texte, mais pas :href propriétés dans (str) pour les faire se développer dynamiquement.

Répondre

5

La macro with-html-output-to-string développe son corps en utilisant special evaluation rules. En particulier, toutes les formes non reconnues sont laissées telles quelles, ce qui signifie que les macros ne sont pas développées avant la génération du code html, ce qui signifie que lorsque votre macro publish-newsfeed est développée par le compilateur standard, elle n'est plus dans le contexte with-html-output-to-string. Cela est clair lors de l'expansion de la macro manually, en particulier en utilisant la fonction macroexpansion slime.

Pour fonctionner, vous devez vous faire publish-newsfeed une fonction et utiliser with-html-output à l'intérieur avec le même flux (soit supposer `partout standard sortie ou passer le flux explicitement).