Поваренная книга Clojure socket Prepl

Ссылка на оригинал - https://oli.me.uk/2019-03-22-clojure-socket-prepl-cookbook/, автор публикации - Oliver Caldwell

Socket prepl - это относительно новый инструмент, встроенный в Clojure, который позволяет вам выполнять REPL в работающем приложении. По сути, это гораздо более простой «официальный» подход к проблеме, который также решает nREPL. Если ваш нынешний инструментарий nREPL вам подходит, тогда этот пост не будет вам очень полезен.

В течение последних шести месяцев я разрабатывал инструментальные средства Clojure (Script) для Neovim через предварительно подготовленное соединение (включая три попытки на разных языках). Это называется Conjure, и вы, возможно, видели, как я постоянно говорил об этом в твиттере. С тех пор, как я написал этот текст, я начал изучать различные среды и приложения, и в итоге я нашел немало рецептов.

Этот пост предназначен для ознакомления с различными методами подготовки сокетов. Я надеюсь, вы сочтете их полезными!

Из CLI

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

clj -J-Dclojure.server.jvm="{:port 5555 :accept clojure.core.server/io-prepl}" \
    -J-Dclojure.server.node="{:port 5556 :accept cljs.server.node/prepl}" \
    -J-Dclojure.server.browser="{:port 5557 :accept cljs.server.browser/prepl}"

Таким образом запускаются три подготовительных этапа, а затем осуществляется переход в обычный сеанс REPL. Сеанс REPL и jvmprepl используют одну и ту же среду, поэтому изменения в CLI REPL влияют на Prepl и наоборот.

node и browser готовят собственные среды после eval. Попробуйте запустить prels, затем используйте netcat (или аналогичный), чтобы отправить им код.

# JVM
nc localhost 5555
(+ 10 10)
{:tag :ret, :val "20", :ns "user", :ms 2, :form "(+ 10 10)"}
:repl/quit

# node
nc localhost 5556
(+ 10 10)
{:tag :ret, :val "20", :ns "cljs.user", :ms 9, :form "(+ 10 10)"}
:repl/quit

# browser
nc localhost 5557
{:tag :ret, :val "20", :ns "cljs.user", :ms 51, :form "(+ 10 10)"}
:repl/quit

Результирующие структуры данных, которые вы видите, являются частью prepl, это то, что делает prepl, оборачивает вещи в предсказуемую структуру данных, с которой могут разбираться и работать такие инструменты, как Conjure.

Из вашего кода

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

(ns my.project.prepl
  (:require [clojure.core.server :as server]))

(defn start-prepl! [{:keys [bind port name]}]
  (server/start-server {:accept 'clojure.core.server/io-prepl
                        :address bind
                        :port port
                        :name name}))

;; In some -main fn somewhere...
(start-prepl! {:bind "localhost", :port 5555, :name "jvm"})

По сути, это то же самое, что и первая строка в подходе только к CLI, например, вы можете поменять местами :accept функцию cljs.server.node/prepl и получить преплин узла.

Попался: несколько узлов

Что-то, что я поймал и в конце концов решил, почему запуск нескольких предварительных проверок узлов на разных портах конфликтовал друг с другом. Что ж, получается, что в исходном коде узла есть жесткий порт (49001, если вам интересно). К счастью, это просто по умолчанию, и вы можете настроить его довольно легко.

# Запустите их в двух терминалах и nc в обоих, только один будет работать.
clj -J-Dclojure.server.nodea="{:port 6661 :accept cljs.server.node/prepl}"
clj -J-Dclojure.server.nodeb="{:port 6662 :accept cljs.server.node/prepl}"

# Вы можете установить аргументы, которые принимает функция accept.
# Это означает, что мы можем настроить порт для функции cljs.server.node/prepl.
clj -J-Dclojure.server.nodea="{:port 6661 :accept cljs.server.node/prepl}"
clj -J-Dclojure.server.nodeb="{:port 6662 :accept cljs.server.node/prepl, :args [{:env-opts {:port 48000}}]}"

С этой настройкой вы сможете одновременно выполнять несколько предварительных проверок узлов на одном компьютере.

Фиговое колесо и препл

Для обычных проектов Clojure приведенной выше информации должно быть достаточно для всех ситуаций. Однако для ClojureScript редко бывают разработки без figwheel, это не крайний случай, это норма. Если вы запустите браузер, предварительно запустив еще одну вкладку для оценки, она не будет иметь тот же контекст, что и figwheel.

К счастью, есть способ перезагрузить figwheel ваш ClojureScript, а также подготовиться к этой среде figwheel! Я получил эту работу figwheel-main и несколько советов от самого Брюса по Slack и Twitter (@bhauman). Вот минимум deps.ednдля этой техники.

{:paths ["src" "target"]
 :deps {org.clojure/clojure {:mvn/version "1.10.0"}
        org.clojure/clojurescript {:mvn/version "1.10.520"}
        com.bhauman/figwheel-main {:mvn/version "0.2.0"}}}

И небольшое количество кода для запуска figwheel, а затем подключите его к figwheel repl-env. Это означает, что вы можете отправить код в сокет prepl, но он будет использовать figwheel для компиляции этого ClojureScript и передачи полученного JavaScript в браузер (или процесс узла!) Для оценки.

(ns pfig.main
  (:require [figwheel.main.api :as fig]
            [clojure.core.server :as server]))

(defn -main []
  (figwheel.main.api/start
    {:id "dev"
     :options {:main 'pfig.test}
     :config {:watch-dirs ["src"]
              :mode :serve}})

  (println "=== START PREPL")
  (server/start-server {:accept 'cljs.core.server/io-prepl
                        :address "127.0.0.1"
                        :port 6776
                        :name "pfig"
                        :args [:repl-env (fig/repl-env "dev")]})

  (fig/cljs-repl "dev"))

Я использую более новую figwheel-main, но это определенно выполнимо в других итерациях figwheel, API может быть немного другим.

:repl/quit

Есть еще советы или комментарии? Скажи привет в твиттере, я @OliverCaldwell. Я надеюсь, что вы узнали что-то новое, хорошего дня!

Изменения за 2019-03-23

Хосе Луис Лафуэнте ( @jlesquembre ) отметил, что вы можете поместить эти аргументы препровождающей JVM в ваш deps.ednфайл, но вам не разрешено использовать пробелы, из-за чего карты Clojure писать сложно. Вы можете обойти эту проблему, заменив пробелы в строке запятыми, поскольку Clojure все равно обрабатывает запятые как пробельные символы.

{:deps {}

 :aliases
 {:prepl {:jvm-opts ["-Dclojure.server.repl={:port,40404,:accept,clojure.core.server/io-prepl}"]}}}

Этот пример взят из github.com/seancorfield/dot-clojure.