Подтвердить что ты не робот

Обмен кодами между сервером и клиентом в Clojurescript/Clojure

Предположим, что я хотел разложить некоторый общий код между моей клиентской стороной *.cljs и моей серверной стороной *.clj, например. различные структуры данных и общие операции, могу ли я это сделать? Имеет ли смысл это делать?

4b9b3361

Ответ 1

Отличный вопрос! В последнее время я много думал об этом и написал несколько экспериментов для экспериментов.

Вот мой список того, какие типы вещей вы можете разделить, а также плюсы и минусы каждого из них:

  • Большинство моих клиентских файлов cljs содержат код, который управляет dom. Таким образом, было бы бессмысленно делиться любым из них с сервером
  • Большая часть материала на стороне сервера связана с вызовами файловой системы и базы данных. Я полагаю, вы могли бы захотеть вызвать базу данных у клиента (особенно если вы используете один из db без sql, поддерживающий javascript-вызовы). Но даже тогда я чувствую, что вы должны выбрать либо вызов db из клиента, либо вызов db с сервера, и поэтому не имеет смысла делиться кодом db.
  • Одной областью, где разделение является, безусловно, ценным, является возможность совместного использования и передачи структур данных clojure (вложенные комбинации списков, векторов, наборов и т.д.) между клиентом и сервером. Не нужно конвертировать в json (или xml) и обратно. Например, очень удобная возможность пропускать представления типа hiccup для дома и обратно. В gwt я использовал gilead для обмена моделями между клиентом и сервером. Но в clojure вы можете просто передавать структуры данных, поэтому нет необходимости делиться определениями классов, как в gwt.
  • Одна из областей, которые, как мне кажется, нужно экспериментировать больше, - это совместное использование состояния между клиентом и сервером. На мой взгляд, существует несколько стратегий: сохранять состояние на клиенте (одностраничные приложения типа ajax) или сохранять состояние на сервере (например, устаревшие приложения jsp) или комбо обоих. Возможно, код, отвечающий за обновление состояния (атомы, ссылки, агенты или что-то еще), может быть разделен, а затем состояние может передаваться взад и вперед по запросу и отклику, чтобы синхронизировать два уровня? До сих пор просто писать сервер, используя лучшие методы REST, а затем иметь состояние, хранящееся на клиенте, кажется, работает очень хорошо. Но я мог видеть, как могут быть преимущества для совместного использования состояния между клиентом и сервером.
  • Мне не нужно было делиться Константами и/или свойствами, но это может быть что-то, что было бы полезно повторить. Если вы поместите все глобальные константы приложения в файл clj, а затем напишите script, чтобы скопировать его в cljs всякий раз, когда вы скомпилировали clojurescript, который должен работать нормально и может сэкономить немного дублирования кода.

Надеюсь, что эти мысли полезны, меня очень интересует то, что другие нашли до сих пор!

Ответ 2

Я написал cljx плагин Leiningen специально для обработки обмена файлами Clojure/ClojureScript для библиотеки визуализации данных Clojure. 95% кода, не связанного с хостом, выглядят одинаково, и cljx позволяет автоматически переписать эти последние 5%, указав правила перезаписи с помощью core.logic. Однако большую часть времени это простые замены символов; clojure.lang.IFn в Clojure - это просто IFn в ClojureScript, например.

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

Ответ 3

Новый плагин lein-cljsbuild для Leiningen имеет встроенную поддержку для обмена чистым кодом Clojure.

Ответ 4

Написал быстрый бит кода, чтобы скопировать подмножество моего сервера clojure кода на мой код clojurescript, переименовав его как .cljs перед созданием:

(ns clj-cljs.build
  (use
    [clojure.java.io]
  )
  (require
    [cljs.closure :as cljsc]
  )
)

(defn list-files [path]
 (.listFiles (as-file path))
)

(defn copy-file* [from to]
 ;(println " coping " from " to " to)
 (make-parents to)
 (copy from to)
)    

(defn rename [to-path common-path f]
 (str to-path common-path (.replaceAll (.getName f) ".clj" ".cljs"))
)

(defn clj-cljs* [files common-path to-path]
  (doseq [i (filter #(.endsWith (.getName %) ".clj") files)]
    (copy-file* i (file (rename to-path common-path i)))
  )
  (doseq [i (filter #(.isDirectory %) files)]
    (clj-cljs* (list-files i) (str common-path (.getName i) "/") to-path)
  )
)

(defn build [{:keys [common-path clj-path cljs-path js-path module-name]}]
  (clj-cljs* (list-files (str clj-path common-path)) common-path cljs-path)
  (cljsc/build
    cljs-path
    {
     :output-dir js-path
     :output-to (str js-path module-name ".js")
    }
  )
)

(defn build-default []
  (build
   {
    :clj-path "/home/user/projects/example/code/src/main/clojure/"
    :cljs-path "/home/user/projects/example/code/src/main/cljs/"
    :js-path "/home/user/projects/example/code/public/js/cljs/"
    :common-path "example/common/" ; the root of your common server-client code
    :module-name "example"
   }
  )
)

Ответ 5

Этот вопрос предшествует cljc, но поскольку я наткнулся на него, я подумал, что я бы назвал Clojure условия для чтения.