Clojure 구성식 연습


IntelliJ IDEA/Cursive IDE의 Clojure 적분기 연습.

소개


작년 12월 제가 Metosin에 가입하기 전에 휴가를 보냈을 때, 저는 제 Clojure 단순 서버 연습을 다시 실시함으로써 제 Clojure 기술을 조금 훈련하기로 했습니다.너는 나의 이전 블로그 글Clojure Impressions Round Three에서 이 연습에 관한 내용을 더 많이 읽을 수 있다.그 블로그 글에서 나는 "응용 프로그램 상태 관리 라이브러리가 없으면 너도 할 수 있다."라고 말했다.나는 여전히 이 주장에 동의한다. 그러나... 내가 작년 1월부터 Metosin에서 일하기 시작했을 때, 나는 Metosin의 사람들과 Clojure 응용 프로그램의 상태 관리를 토론했다. 대부분의 Metosin Clo 법학자들이 사용하고 있다Integrant.자신의 주 관리 (지난 연습에서 했던 것처럼) 를 만들거나 주 관리 라이브러리를 사용할지 여부에 대해 대화를 나누었습니다.이런 대화에 의하면 이 사람들이 정말 좋은 복제 법학자이고, 그들이 한 주의 관리 도서관을 사용하고 있는 이상, 그들을 사용하는 데는 틀림없이 믿을 만한 이유가 있을 것이라는 것을 깨달았다.그래서 저는 Clojure 단순 서버 연습을 다시 한 번 실현하기로 했습니다. 이번에는 Integrant를 사용합니다.이 동시에 나는 코드를 더욱 간단하게 재구성하고 새로운 데이터 저장소인 Postgres를 추가했다. (이전에는 가상 CSV와 DynamoDB가 데이터 저장 옵션으로 있었다.)재구성 작업도 Integrant와 양호한 상태 관리 라이브러리를 사용하여 재구성 작업을 어떻게 도와주는지 좋은 테스트 플랫폼이다.
이 항목은 Github 에서 찾을 수 있습니다.
면책 성명: 이것은 Integrant를 어떻게 사용하는지 배워서 상태 처리가 있는 Clojure 개발을 더욱 순조롭게 하는 빠른 개인 연습일 뿐이다.이 연습은 결코 완벽한 예가 아니다. 어떻게 인터넷 상점이나 인터넷 응용 프로그램을 만드는가. - 많은 특수한 역사적 원인이 있다. (예를 들어 지역 실체는 간단한 벡터를 통해 전달된다. - 나는 지도를 사용해야 한다.)

왜 피적체입니까?


Integrant 또는 기타 상태 관리 라이브러리를 직접 처리하지 않고 사용해야 하는 이유는 무엇입니까?지난 블로그에서 말한 바와 같이 Clojure는 매우 유연합니다. 예를 들어 간단한 atom 를 사용하면 상태를 쉽게 처리할 수 있습니다.그러나 Integrant와 같은 좋은 상태 관리 라이브러리는 더 많은 것을 제공한다.Integrant를 사용하면 간단한 edn 파일을 가지고 구성 요소를 관리할 수 있으며, 그것들이 어떻게 서로 연결되어 응용 프로그램에서 전체적인 상태를 형성할 수 있습니다.적분기의 또 다른 강력한 기능은 서로 다른 목적을 위한 다양한 상태(예를 들어 생산된 상태와 테스트를 실행하는 상태)를 쉽게 만들고 초기화하는 것이다.다음 장에서 이러한 기능을 전개합시다.

Edn의 피적 상태로 구성


상태의 초기 설정을 제공하고 서로 다른 구성 요소와 상호작용을 사용하여 상태를 구축하는 구성 파일을 만들 수 있습니다.이거 좋아요.config.edn 파일을 봅니다.보시다시피 저는 csv,dynamodb,postgres, 그리고 그 중의 모든 통합 설정을 만들었습니다.
 :backend/csv {:profile #ig/ref :backend/profile
               :active-db #ig/ref :backend/active-db
               :data-dir "dev-resources/data"}

 :backend/ddb {:active-db #ig/ref :backend/active-db
               :ss-table-prefix "ss"
               :ss-env #profile {:prod "prod"
                                 :dev "dev"
                                 :test "test"}
               :endpoint {:protocol :http :hostname "localhost" :port 8000}
               :aws-profile "local-dynamodb"}

 :backend/postgres {:active-db #ig/ref :backend/active-db
                    :adapter "postgresql"
                    :username #or [#env DB_USERNAME "simpleserver"]
                    :password #or [#env DB_PASSWORD "simpleserver"]
                    :server-name #or [#env DB_HOST "localhost"]
                    :port-number #long #or [#env DB_PORT 5532]
                    :database-name #or [#env DB_NAME "simpleserver"]
...

그리고 서비스 구성 요소는 이러한 데이터 저장 구성 요소를 참조합니다.
 ; Gather different data store services here.
 :backend/service {:profile #ig/ref :backend/profile
                   :active-db #ig/ref :backend/active-db
                   :csv #ig/ref :backend/csv
                   :ddb #ig/ref :backend/ddb
                   :postgres #ig/ref :backend/postgres
                   }

나는 다른 구성 요소, 제티, nrepl 등을 생략했다. 이런 기능을 만드는 데는 많은 노력이 필요하다.물론 당신의 상태가 간단하다면 이 기능을 필요로 하지 않겠지만, 당신의 상태에 많은 구성 요소가 있고 서로 다른 상호작용이 있을 때 이 기능은 매우 좋습니다.
읽기 피적 설정에 대해서는 Aero 를 사용합니다.Aero는 구성 요소와 메커니즘 내의 값에 환경 변수를 주입하여 간단한 조건과 문자열에서 숫자로의 변환에 사용합니다(예: :port-number #long #or [#env DB_PORT 5532]. 즉, 환경 변수 DB_PORT를 사용하고 있습니다. 찾을 수 없으면 기본값 5532를 사용하고 문자열 값을 long으로 변환합니다).

피적의 다른 상태를 가지고 있다


피적 함수의 또 다른 강력한 기능은 서로 다른 목적을 위해 서로 다른 상태를 만드는 것이다.E, g. 이 연습에서 나는 두 가지 상태를 만들었는데 하나는 생산(및 개발)에서 시스템을 운행하는 데 사용되고 다른 하나는 운행 테스트에 사용된다.운영/개발 상태 읽기config.edn 구성 및 core.clj 파일에서 실제 상태를 구성합니다.
E, g. 서로 다른 데이터 저장소는 매우 다른 상태로 표시됩니다.
(defmethod ig/init-key :backend/csv [_ {:keys [profile active-db data-dir]}]
  (log/debug "ENTER ig/init-key :backend/csv")
  ; We simulate this data store using atom.
  ; We initialize the "db" from :data-dir.
  (if (= active-db :csv)
    (let [csv-data
          {:data-dir data-dir
           :db (atom {:domain {}
                      :session #{}
                      :user {}})}]
      ; Let's keep the test database empty.
      (if (not= profile :test)
        (csv-db-loader/load-csv-db csv-data))
      (:db csv-data))))

(defmethod ig/init-key :backend/ddb [_ {:keys [active-db ss-table-prefix ss-env endpoint aws-profile]}]
  (log/debug "ENTER ig/init-key :backend/ddb")
  (if (= active-db :ddb)
    (ddb-config/get-dynamodb-config ss-table-prefix ss-env endpoint aws-profile)))

(defmethod ig/init-key :backend/postgres [_ opts]
  (log/debug "ENTER ig/init-key :backend/postgres")
  (if (= (:active-db opts) :postgres)
    {:datasource (hikari-cp/make-datasource (dissoc opts :active-db)) :active-db (:active-db opts)}))

csv 파일에서 "csv"데이터베이스를 초기화하고 간단한 Clojure atom "아날로그"데이터베이스를 사용합니다.DynamoDB를 데이터 저장소로 사용할 때 함수 초기화 상태를 요구합니다.PostgreSQL 데이터 저장소를 사용할 때 데이터베이스 연결을 초기화하기 위해 get-dynamodb-config 라이브러리만 사용합니다.
test_config.clj 파일은 테스트 상태 초기화를 포함합니다.주로 실제 상태 초기화만 다시 사용하지만 몇 가지 값을 덮어씁니다.
(defn test-config []
  (let [test-port (random-port)
        ; Overriding the port with random port, see TODO below.
        _ (log/debug (str "test-config, using web-server test port: " test-port))]
    (-> (core/system-config :test)
        ;; Use the same data dir also for test system. It just initializes data.
        (assoc-in [:backend/csv :data-dir] "dev-resources/data")
        (assoc-in [:backend/jetty :port] test-port)
        ;; In Postgres test setup use simpleserver_test database.
        (assoc-in [:backend/postgres :database-name] "simpleserver_test")
        ;; No nrepl needed in tests. If used, use other port than the main system.
        (assoc-in [:backend/nrepl :bind] nil)
        (assoc-in [:backend/nrepl :port] nil)
        (assoc-in [:backend/csv :port] nil))))

E, g. 우리는 Jetty를 위해 서로 다른 포트를 사용해서 Jetty를 동시에 실행하여 개발과 테스트를 할 수 있고 포트가 충돌하지 않도록 한다.테스트에 대해서는 PostgreSQL 데이터베이스도 다릅니다.

상태 재설정


적분기는 상태를 재설정하는 데 좋은 보조 기능을 제공합니다. go,reset,halt 등입니다. Integrant Repl documentation에서 이 기능들의 의미를 확인하십시오.나는 가장 자주 사용하는 보조 기능을 위해 Cursive 핫키를 만들었다. 예를 들어 hikari-pc is(integrant.repl/reset).따라서 이름 공간에서 변경을 할 때hitalt-J Integrant는 영향을 받은 이름 공간을 다시 불러오고 상태를 초기화합니다.이것은 눈 깜짝할 사이에 발생하기 때문에 나는 Clojure를 할 때 자주 때린다alt-J.
개발 상태를 실행하면 상태를 조회할 수 있습니다.
(user/env)
=>
{:profile :dev,
 :active-db :postgres,
 :service {:domain #simpleserver.service.domain.domain_postgres.PostgresR{:db {:datasource #object[com.zaxxer.hikari.HikariDataSource
                          0x42ab0edd
                          "HikariDataSource (HikariPool-33)"],
...

테스트 시작 테스트 상태는 수동으로 시작하고 테스트 상태를 조회할 수 있습니다.
(simpleserver.test-config/go)
(simpleserver.test-config/test-env)
=>
{:profile :test,
 :active-db :postgres,
 :service {:domain #simpleserver.service.domain.domain_postgres.PostgresR{:db {:datasource #object[com.zaxxer.hikari.HikariDataSource
                          0x780ac829
                          "HikariDataSource (HikariPool-35)"],
...

보시다시피 alt-J 이 상태에서는 다른 대상입니다.
이거 좋아요.나는 개발 상태에 도달하기 위해 보통 나의 임시 저장 문서에서 다른 것을 실험한다.내가 기뻐할 때, 나는 코드를 초고 파일에서 생산 코드로 옮길 것이다.만약 내가 어떤 테스트에서 문제가 발생한다면, 나는 수동으로 테스트 설정을 시작하고, 테스트 코드에서 폼을 보내서 REPL에서 실행한 후에 폼이 테스트 상태로 들어갈 수 있다.

테스트 실행


나는 DynamoDB와Postgres 데이터베이스에 Just 개의 식단을 제공했다.그것들을 시작하고 테스트를 실행하자...
λ> just
Available recipes:
    backend # Start backend repl.
    backend-kari # Start backend repl with my toolbox.
    dynamodb # Start local dynamodb emulator
    lint # Lint
    list
    postgres # Start local postgres
    test db # Test

# First Postgres...
λ> just postgres
NOTE: Remember to destroy the container if running again!
Starting docker compose...
Creating ss-postgres_postgres_1 ... done
Creating Simple Server schemas...
...

# ...and then DynamoDB...
λ> just dynamodb
Sending build context to Docker daemon 62.11MB
Step 1/15 : FROM python:3.8.2-slim-buster
 ---> e7d894e42148
Step 2/15 : RUN rm /bin/sh && ln -s /bin/bash /bin/sh
 ---> Using cache
...
Successfully tagged ss-uploader:0.1
Creating ss-dynamodb_local-dynamodb_1 ... done
Creating ss-dynamodb_uploader-app_1 ... done
Attaching to ss-dynamodb_local-dynamodb_1, ss-dynamodb_uploader-app_1
...

IntelliJ IDEA/Cursive에서 테스트를 실행하려면 config.edn 에서 손쉽게 활성 데이터 저장소를 설정할 수 있습니다.
 ; csv, ddb, postgres
 ;:backend/active-db #or [#env SS_DB :csv]
 ;:backend/active-db #or [#env SS_DB :ddb]
 :backend/active-db #or [#env SS_DB :postgres]
...

... 그런 다음 피적 상태를 재설정하고 IntelliJ IDEA/Cursive에서 테스트를 실행합니다.

IntelliJ IDEA/Cursive IDE의 Clojure 적분기 연습에서 테스트를 실행합니다.
구성에서 SimpleServer DB 플래그십HikariDataSource이 있는 경우 사용합니다.따라서 위에서 건의한 바와 같이 명령줄에서 테스트 세트를 실행하여 명령Justfile으로 데이터 저장을 제공할 수 있습니다.
# Test
@test db:
    ./run-tests.sh 

...및run-tests.shhelper, Just 명령에 사용된 내용에 따라 SS_DB 플래그를 채웁니다.
...
MYDB=$1
if [["$MYDB" =~ ^(csv|ddb|postgres)$]]; then
    echo "Starting tests with $MYDB configuration..."
else
    echo "Unknown DB configuration: $MYDB, exiting..."
    exit 2
fi
SS_DB=$MYDB clojure -A:dev:test:common:backend -m kaocha.runner

테스트를 실행합니다.
# First using DynamoDB as data store...
λ> just test ddb
Starting tests with ddb configuration...
[(........)(...........................)(...)(.......)]
14 tests, 45 assertions, 0 failures.
# ...and then Postgres:
λ> just test postgres
Starting tests with postgres configuration...
[(...)(...........................)(.......)(........)]
14 tests, 45 assertions, 0 failures.

Integrant는 애플리케이션이 올바른 데이터 스토리지를 사용할 수 있도록 테스트 상태를 구성합니다.

결론


나는 내가 이 포인트 연습을 해서 매우 기쁘다.나는 현재 고품질의 상태 관리 라이브러리를 사용하는 것이 자신이 상태 관리를 실시하는 것보다 낫다는 것을 깨달았다.Lisp의 Clojure로서 매우 강력하고 유연하기 때문에 자신도 쉽게 상태 관리를 할 수 있습니다.그러나 고품질의 상태 관리 라이브러리를 사용하면 기본적인 충전을 무료로 제공할 수 있기 때문에 이 점을 걱정할 필요 없이 상태를 설정으로 작성하고 개발 과정에서 서로 다른 목적을 위해 서로 다른 상태를 사용할 수 있습니다.
작성자는 클라우드 프로젝트에서 Clojure를 사용하여 Metosin에서 작업하고 있습니다.만약 핀란드에서 Clojure 프로젝트를 시작하거나 핀란드에서 Clojure 교육을 받는 것에 관심이 있다면, 제 메일박스에 이메일을 보내거나 LinkedIn을 통해 연락 주십시오.
Kari Marttila
  • Kari Marttila LinkedIn 홈페이지:
  • 좋은 웹페이지 즐겨찾기