2017-01-11 2 views
1

У меня есть следующий код Clojure с render Функция, которая отображает html-страницу с помощью enlive-html. В зависимости от выбранного языка используется другой шаблон html.Как уменьшить дублирование в коде Clojure ниже?

Как вы можете видеть, существует много дубликатов кода, и я хотел бы удалить его.

Я думал о написании некоторых макросов, но, если я правильно понимаю, язык (т.е. параметр lang) недоступен во время выполнения макроса, поскольку он предоставляется в запросе и находится во время выполнения, а не во время компиляции ,

Я также попытался изменить запрос, чтобы добавить поддержку i18n в некоторый более поздний момент, но мои навыки Clojure еще не установлены.

Так вопросы:

Как я могу удалить дублирование кода в коде ниже?

Is enlive-html способ пойти или использовать другую библиотеку? Есть ли библиотека, похожая на призыв с поддержкой i18n?

Спасибо!

Смотрите код здесь:

(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)) 
+0

Что именно вы имеете в виду, re: «Язык недоступен при выполнении макросов»? –

+0

... если ваши макросы генерируют ваш 'deftemplate', вам нужно всего лишь запустить их во время компиляции, чтобы выполнить это выполнение. –

+3

Это относится к http: // codereview.stackexchange.com/, потому что он уже работает. – tar

ответ

1

С одной стороны, это не слишком сложно, чтобы написать макрос, который будет генерировать точный код, который вы имеете здесь для произвольного множества языков. С другой стороны, есть, вероятно, лучший подход, чем использование deftemplate - вещи, которые есть def d - это то, что вы ожидаете назвать по имени в исходном коде, тогда как вы просто хотите, чтобы эта вещь была создана и использовалась автоматически. Но я не знаком с API-интерфейсом, поэтому не могу сказать, что вы должны делать.

Если вы решили придерживаться макрос вместо этого, вы могли бы написать что-то вроде:

(defmacro def-language-404s [languages] 
    `(do 
    [email protected](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# 
          [email protected](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)))) 
+0

Спасибо за ответ. Макрос будет в порядке, по крайней мере пока, но тот, в вашем ответе, не работает для меня. Он жалуется на конструкцию [: #path] См. Здесь: https://gist.github.com/anonymous/ec0f2b18269f28d5d1c333945cf386ac – vidi

+0

А? ': # path' скопирован дословно из вашего вопроса; мой макрос расширяется в нужный вам код. Я не знаю, что такое сделка с вашими ошибками, но они не связаны с ': # path', и действительно, они даже не упоминают об этом. Вы копировали/вставляли неправильно? Заменила ли загрузочная реплика? Кто знает, но сообщение об ошибке не соответствует коду. – amalloy

+0

Я не копировал пасту неправильно. Как boot, так и lein используют одну и ту же версию nrepl, поэтому я не думаю, что это связано с загрузкой. Я пробовал оба Clojure v1.7.0 и 1.8.0, и он ведет себя одинаково. Я сказал выше, что проблема связана с символом # в: #path, потому что если я удалю #, ошибка больше не будет. Я предполагаю, что, поскольку # имеет значение внутри макроса, он должен быть экранирован или что-то еще, но я не нашел, как это сделать. См. Этот gist https://gist.github.com/anonymous/312877d8e15a0f5791ff9bab759a203f – vidi

0

После совсем немного макро-фу я получил в результате, что я доволен. С некоторой помощью нескольких хороших stackoverflowers я написал следующие макросы поверх enlive:

(ns hello-enlive 
    (:require [net.cgrand.enlive-html :refer [deftemplate]])) 

(defn- template-name [lang page] (symbol (str "-template-" (name page) "-" (name lang) "__"))) 
(defn- html-file [lang page] (str (name lang) "/" (name page) ".html")) 
(defn- page-fun-name [page] (symbol (str "-page" (name page)))) 

(defmacro def-page [app languages [page & forms]] 
    `(do 
    [email protected](for [lang languages] 
     `(deftemplate ~(template-name lang page) ~(html-file lang page) 
      [email protected])) 

     (defn ~(page-fun-name page) [lang#] 
     (case lang# 
      [email protected](for [lang languages 
        clause [(keyword lang) (template-name lang page)]] 
       clause))) 

     (def ^:dynamic ~app 
     (assoc ~app ~page ~(page-fun-name page))) 
    )) 

(defmacro def-app [app-name languages pages] 
    (let [app (gensym "app__")] 
    `(do 
     (def ~(vary-meta app merge {:dynamic true}) {}) 

     [email protected](for [page# pages] 
      `(def-page ~app ~languages ~page#)) 

     (defn ~app-name [lang# [page# params#]] 
     (apply (apply (get ~app page#) [lang#]) params#))))) 

... которые затем используются как это:

HTML-шаблоны хранятся в виде дерева, как этот

html/fr/not-found.html 
html/fr/index.html 
html/en/not-found.html 
html/en/index.html 
... 

... и логика рендеринга выглядит следующим образом:

(def-app my-app [:en :it :fr :de] 
    [ [:page/index [] ] 

    ;... put your rendering here 

    [:page/not-found [{path :path}] 
     [:#path] (content path)]]) 

. ..и внешний вид использования, как это:

... 
(render lang [:page/index {}]) 
(render lang [:page/not-found {:path path}]) 
... 

В результате, несмотря на то, что, вероятно, может быть улучшен, я думаю, это очень приятно, без дублирования и шаблонного кода.

Смежные вопросы