clojureenlive

How can I reduce the duplication in the Clojure code below?


I have the following Clojure code with a render function which renders a html page using enlive-html. Depending on the selected language a different html template is used.

As you can see, there is a lot of code duplication and I would like to remove it.

I was thinking of writing some macros but, if I understand correctly, the language (i.e. lang parameter) is not available at macro execution time because it is provided in the request and that is at execution time and not at compilation time.

I also tried to modify enlive in order to add i18n support at some later point but my Clojure skills are not there yet.

So the questions are:

How can I remove the code duplication in the code below?

Is enlive-html the way to go or should I use another library? Is there a library similar to enlive with support for i18n?

Thanks!

See the code here:

(ns myapp.core
  (:require [net.cgrand.enlive-html :as e))

(deftemplate not-found-en "en/404.html"
  [{path :path}]
  [:#path] (e/content path))

(deftemplate not-found-fr "fr/404.html"
  [{path :path}]
  [:#path] (e/content path))


(defn getTemplate [page lang]
  (case lang
      :en (case page
                :page/not-found not-found-en)
      :fr (case page
                :page/not-found not-found-fr)))

(defn render [lang [page params]]
  (apply (getTemplate page lang) params))

Solution

  • On the one hand, it is not too hard to write a macro that will generate the exact code you have here for an arbitrary set of languages. On the other hand, there is probably a better approach than using deftemplate - things that are defd are things you expect to refer to by name in the source code, whereas you just want this thing created and used automatically. But I'm not familiar with the enlive API so I can't say what you should do instead.

    If you decide to stick with the macro instead, you could write something like:

    (defmacro def-language-404s [languages]
      `(do
         ~@(for [lang languages]
             `(deftemplate ~(symbol (str "not-found-" lang)) ~(str lang "/404.html")
                [{path# :path}]
                [:#path] (e/content path#)))
         (defn get-template [page# lang#]
           (case page#
             :page/not-found (case lang#
                               ~@(for [lang languages
                                       clause [(keyword lang)
                                               (symbol (str "not-found-" lang))]]
                                   clause))))))
    
    user> (macroexpand-1 '(def-language-404s [en fr]))
    (do
      (deftemplate not-found-en "en/404.html"
        [{path__2275__auto__ :path}]
        [:#path] (content path__2275__auto__))
      (deftemplate not-found-fr "fr/404.html"
        [{path__2275__auto__ :path}]
        [:#path] (content path__2275__auto__))
      (defn get-template [page__2276__auto__ lang__2277__auto__]
        (case page__2276__auto__
          :page/not-found (case lang__2277__auto__
                            :en not-found-en
                            :fr not-found-fr))))