4

Trying to load a particular template based on what :server-name returns in the request:

(ns rosay.views.common
  (:use noir.core)
  (:require [noir.request :as req]
            [clojure.string :as string]
            [net.cgrand.enlive-html :as html]))

(defn get-server-name
  "Pulls servername for template definition"
  []
  (or (:server-name (req/ring-request)) "localhost"))

(defn get-template
  "Grabs template name for current server"
  [tmpl]
  (string/join "" (concat [(get-server-name) tmpl])))

(html/deftemplate base (get-template "/base.html")
  []
  [:p] (html/content (get-template "/base.html")))

It works for localhost which returns /home/usr/rosay/resources/localhost/base.html, but when I test against a different host say "hostname2" I see where get-template is looking at /home/usr/rosay/resources/hostname2/base.html but when it renders in the browser it always points back to ../resources/localhost/base.html.

Is there a macro or different way to handle this use-case?

battlemidget
  • 249
  • 1
  • 4
  • 15
  • The problem is that deftemplate is a macro, so it's evaluated at compile time. At that point (:servername (req/ring-request)) is nil, so "localhost" will be hardcoded into the class files generated for your view. – Diego Basch Dec 08 '12 at 02:20
  • So speaking with someone on irc they mentioned the possibilty of using 'at', however, there are some performance considerations due to recompiling the template on each request – battlemidget Dec 08 '12 at 02:29

1 Answers1

2

As mentioned in the comments, deftemplate is a macro that defines the template as a function in your namespace - only once, when it's first evaluated. You can easily write some code to lazily create the templates, and eliminate some of the overhead by caching the template once it's created:

(def templates (atom {}))

(defmacro defservertemplate [name source args & forms]
  `(defn ~name [& args#]
     (let [src# (get-template ~source)]
       (dosync
        (if-let [template# (get templates src#)]
          (apply template# args#)
          (let [template# (template src# ~args ~@forms)]
            (swap! templates assoc src# template#)
            (apply template# args#)))))))

In your case you'd then be able to say (defservertemplate base "/base.html"....

You can probably tidy this up a bit. All you really need to know is that deftemplate just calls template, and you can use that directly if you want.

Thom
  • 2,643
  • 2
  • 30
  • 33