Споделяне на код между сървър и клиент в Clojurescript/Clojure

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


person Hendekagon    schedule 20.10.2011    source източник
comment
Наистина би имало смисъл, както в GWT можете също да споделяте Java код на сървъра и на клиента. Ще бъде много готино да чуя отговора на това!   -  person Michiel Borkent    schedule 20.10.2011
comment
единственият начин, за който се сещам в момента, е да поставя споделения код в пространство на имената на сървъра и структура на директория, след което да добавя редове към вашия код за компилиране, за да копирате файловете в директорията източник на клиента, преименувайки като *.cljs - защото компилаторът на clojurescript търси само файлове с имена .cljs   -  person Hendekagon    schedule 21.10.2011


Отговори (5)


Актуализация: от clojure 1.7 вижте Clojure reader условни или cljc. Използвах cljc с голям успех, за да споделя много лесно много код между сървър и браузър.

Страхотен въпрос! Напоследък също мислих много за това и написах няколко приложения, за да експериментирам.

Ето списъка ми с видовете неща, които бихте искали да споделите, както и плюсовете/минусите на всяко от тях:

  • Повечето от моите клиентски cljs файлове съдържат код, който манипулира dom. Така че не би имало смисъл да споделяте нищо от това със сървъра
  • Повечето от нещата от страна на сървъра се занимават с извиквания на файлова система и база данни. Предполагам, че може да искате да извикате базата данни от клиента (особено ако използвате един от no-sql db, които поддържат javascript извиквания). Но дори и тогава смятам, че трябва да изберете да извикате db от клиент или да извикате db от сървър и следователно няма много смисъл да споделяте db кода.
  • Една област, в която споделянето определено е ценно, е възможността за споделяне и предаване на структури от данни clojure (вложени комбинации от списъци, вектори, набори и т.н.) между клиент и сървър. Няма нужда да конвертирате в json (или xml) и обратно. Например, възможността да предавате представяния на dom напред и назад в стил хълцане е много удобно. В gwt използвах Gilead за споделяне на модели между клиент и сървър. Но в clojure можете просто да предавате структури от данни, така че наистина няма нужда да споделяте дефиниции на класове като в gwt.
  • Една област, в която чувствам, че трябва да експериментирам повече, е споделянето на състояние между клиент и сървър. В съзнанието ми има няколко стратегии: състояние на съхранение на клиент (приложения от тип ajax с една страница) или състояние на съхранение на сървър (като наследени jsp приложения) или комбинация от двете. Може би кодът, отговорен за актуализиране на състоянието (атомите, референтните файлове, агентите или каквото и да е друго), може да бъде споделен и след това състоянието може да бъде предавано напред-назад през заявка и отговор, за да поддържат двете нива в синхрон? Досега простото писане на сървър с помощта на най-добрите REST практики и след това съхраняването на състоянието на клиента изглежда работи доста добре. Но можех да видя как може да има ползи от споделянето на състояние между клиент и сървър.
  • Все още не е необходимо да споделям константи и/или свойства, но това може да е нещо, което би било добре да се използва повторно. Ако поставите всички глобални константи на приложението си в clj файл и след това напишете скрипт, за да го копирате в cljs всеки път, когато компилирате clojurescript, това трябва да работи добре и може да спести малко дублиране на код.

Надявам се тези мисли да са полезни, много ми е интересно какво са открили другите досега!

person Upgradingdave    schedule 25.10.2011
comment
Отлични точки! Бих искал да унифицирам процеса, като накарам моя сървърен код да изгради автоматично целия ми код на Clojurescript, като предаде подмножество от неговите функции в компилатора. Все още не съм чел кода на компилатора, но трябва да е възможно да му подавам изрази директно, без да използвам .cljs файлове. - person Hendekagon; 26.10.2011
comment
Що се отнася до споделянето на структура на данни, библиотеката fetch изглежда доста чиста! github.com/ibdknox/fetch - person leontalbot; 06.08.2014

Написах приставката cljx Leiningen специално за обработка на споделянето на код на Clojure/ClojureScript за библиотека за визуализация на данни на Clojure. 95% от кода, който не е за взаимодействие с хост, изглежда по същия начин и cljx ви позволява автоматично да пренапишете последните 5%, като посочите правила за пренаписване с помощта на core.logic. През повечето време обаче това са прости замествания на символи; clojure.lang.IFn в Clojure е просто IFn в ClojureScript, например.

Можете също да използвате метаданни, за да коментирате формуляри, които да бъдат включени или изключени, когато се генерира код за конкретна платформа.

person Kevin L.    schedule 29.04.2012
comment
cljx е приет като предпочитан инструмент за споделяне на код от cljsbuild - person Stephen Nelson; 04.03.2014

Новият плъгин lein-cljsbuild за Leiningen има вграден поддръжка за споделяне на чист код на Clojure.

person Evan    schedule 19.02.2012
comment
Вградената поддръжка на cljsbuild (кросоувъри) е отхвърлена в полза на cljx, вижте това отговор. - person Stephen Nelson; 04.03.2014

Написах бърз код, за да копирам подмножество от моя сървър 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"
   }
  )
)
person Hendekagon    schedule 02.11.2011

Този въпрос е предшестващ cljc, но тъй като случайно попаднах на него, реших да спомена Clojure условни четеци.

person nha    schedule 17.01.2017