23252826a0
- New chapter 05-clojure-nim.md (EN + BG) covering: - Native compilation pipeline (Clojure → Nim → C → binary) - AI-powered development (error explanation, code generation) - JSON REPL for AI agents - loop/recur with real TCO - Cross-compilation: JS, shared libs, WASM - Persistent data structures (HAMT) - Concurrency: atoms, agents, channels - Updated book README.md with Clojure/Nim focus - Added Clojure/Nim terms to subject indices (EN + BG) - Removed books/ from .gitignore so it can be pushed to GitLab
14 KiB
14 KiB
Чист Clojure: Структура на проекта и инструменти
Съдържание
- Структура на проекта
- deps.edn и CLI инструменти
- Инфраструктура за тестване
- Работен процес за разработка
- Инструменти за качество на кода
- Изграждане и деплойване
- Екосистема от библиотеки
- Техники за дебъгване
- Индекс
1. Структура на проекта
1.1 Типична структура на проект
myproject/
├── deps.edn
├── src/
│ └── myproject/
│ ├── core.clj
│ ├── util.clj
│ └── spec/
│ └── core_spec.clj
├── test/
│ └── myproject/
│ ├── core_test.clj
│ └── util_test.clj
├── resources/
├── doc/
│ └── intro.md
└── README.md
1.2 Namespace като файлови пътища
Namespaces се мапват към файлови пътища:
;; src/myproject/core.clj
(ns myproject.core)
;; src/myproject/util.clj
(ns myproject.util)
;; src/myproject/spec/user.clj
(ns myproject.spec.user)
1.3 Multi-module проекти
;; deps.edn с множество модули
{:paths ["src" "modules/common/src"]
:deps {org.clojure/clojure {:mvn/version "1.11.1"}}
:aliases
{:dev {:extra-paths ["test"]
:extra-deps {}}}}
1.4 Разделяне на Source и Test
- Source код: директория
src/ - Тестове: директория
test/ - И двете се добавят към classpath по време на разработка
2. deps.edn и CLI инструменти
2.1 Референция на deps.edn
{:deps {org.clojure/clojure {:mvn/version "1.11.1"}
org.clojure/data.json {:mvn/version "2.4.0"}
clojure.java-time/clojure.java-time {:mvn/version "1.4.2"}
}
:paths {:src ["src"]
:test ["test"]}
:aliases
{:test {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "1.1.1"}}}
:dev {:jvm-opts ["-Xmx4g"]}
:bench {:extra-deps {criterium/criterium {:mvn/version "0.4.4"}}}}
2.2 Пускане на код
# Пускане на скрипт
clj -M script.clj
# Пускане с test alias
clj -M:test -m myproject.test-runner
# Пускане на REPL с deps
clj -M
# Пускане с конкретни deps
clj -Sdeps '{:deps {org.clojure/clojure {:mvn/version "1.11.1"}}}'
2.3 Разбиране на aliases
Aliases модифицират classpath или поведение:
# Тест с generative проверка
clj -M:test:gen
# Production build
clj -M:prod build
# Dev mode с допълнителни проверки
clj -M:dev
2.4 Създаване на зависимости
;; deps.edn
{:deps {io.github.clojure/data.json {:git/sha "..."}}}
;; Classpath
clj -Sdescribe # Покажи ефективния classpath
3. Инфраструктура за тестване
3.1 test.check за генеративно тестване
(require '[clojure.test.check :as tc]
'[clojure.test.check.generators :as gen]
'[clojure.test.check.properties :as prop])
(def prop-sort-idempotent
(prop/for-all
[v (gen/vector gen/int)]
(= (sort v) (sort (sort v)))))
(tc/quick-check 100 prop-sort-idempotent)
;; => {:result true, :pass? true, ...}
3.2 Fixtures за setup/teardown
(ns myproject.test-util
(:require [clojure.test :as t]
[myproject.db :as db]))
(defn with-test-db [f]
(db/connect! :test)
(f)
(db/disconnect!))
(defn with-logging [f]
(println "Преди тест")
(f)
(println "След тест"))
;; Прилагане на fixtures
(t/use-fixtures :each with-test-db with-logging)
(t/use-fixtures :once db/setup-once)
3.3 Тестови namespace-и
(ns myproject.core-test
(:require [clojure.test :as t]
[myproject.core :as core]))
(t/deftest ^:integration api-test
"Интеграционни тестове за външно API"
...)
;; Пускане на конкретни тестове
(t/run-tests 'myproject.core-test)
(t/run-tests #"myproject.*test")
3.4 Expectations-style тестване
;; Използване на expectations библиотека
(require '[expectations :refer [expect]])
(expect 4 (+ 2 2))
(expect ArithmeticException (/ 1 0))
(expect [:a :b :c] (filterv odd? [1 2 3 4]))
3.5 Midje (за четимост)
(require '[midje.sweet :refer [fact facts =>]])
(fact "събирането работи"
(+ 2 2) => 4)
(facts "за низовете"
(fact "upper-case работи"
(clojure.string/upper-case "hello") => "HELLO"))
4. Работен процес за разработка
4.1 REPL-driven разработка
;; Стартиране на REPL
user=> (require '[myproject.core :as core] :reload)
;; Редактиране на код, презареждане
user=> (require '[myproject.core :as core] :reload :verbose)
;; Презареждане на всички променени namespaces
user=> (require '[clojure.tools.namespace.repl :as ns]
:refer [refresh refresh-all])
user=> (refresh)
4.2 Hot Loading на код
;; В REPL
(def server (atom nil))
(defn start-server []
(swap! server assoc :running true))
;; След редактиране на core, просто презаредете
(require '[myproject.core :as core] :reload)
4.3 Проследяване на source
;; Проследяване на променени файлове
(require '[clojure.java.io :as io]
'[clojure.tools.namespace.track :as track])
(defn track-changes []
(let [tracker (atom (track/tracker))]
(fn []
(swap! tracker track/step)
(track/deps @tracker))))
4.4 Богати comment блокове
(defn process-data [input]
{:pre [(sequential? input)]}
(mapv inc input))
(comment
;; Експерименти по време на разработка
(process-data [1 2 3])
;; Тестване на гранични случаи
(process-data [])
;; Интерактивно дебъгване
(def test-data (load-data))
)
5. Инструменти за качество на кода
5.1 eastwood (Linter)
;; deps.edn alias
:lint {:extra-deps {lancem待/clojure-eastwood {:mvn/version "..."}}}
;; Пускане
clj -M:lint -m eastwood.lint
5.2 clj-kondo (Бърз Linter)
# Инсталиране
brew install clj-kondo
# Пускане върху проект
clj-kondo --lint src/
5.3 Форматиране с cljfmt
;; deps.edn
:format {:extra-deps {com.github.cljfmt/cljfmt {:mvn/version "..."}}}
# Проверка
clj -M:format check src/
# Поправка
clj -M:format fix src/
5.4 Typed Clojure (Незадължително проверяване на типове)
(require '[clojure.core.typed :as t])
(t/ann my-function [t/Int -> t/Int])
(defn my-function [x]
(inc x))
;; Проверка на типовете
(t/check-ns)
6. Изграждане и деплойване
6.1 Изграждане на Uberjars
;; tools.build
(require '[clojure.tools.build.api :as b])
(def lib 'com.mycompany/myapp)
(def version "1.0.0")
(def target-dir "target")
(defn uberjar [opts]
(b/java-command
{:basis (:basis opts)
:main 'myproject.core
:jar-file (b/path target-dir "myapp.jar")
:uber-jar true}))
6.2 Native Image с GraalVM
# Компилиране на Clojure до JVM bytecode първо
clojure -M:build compile
# Изграждане на native image
native-image --initialize-at-build-time=clojure.lang.RT \
-jar myapp.jar
6.3 Docker интеграция
FROM clojure:openjdk-17-lein
WORKDIR /app
COPY deps.edn .
RUN clj -M -P
COPY src src
COPY test test
ENVlein uberjar
ENTRYPOINT ["java", "-jar", "target/myapp.jar"]
6.4 CI/CD Pipeline
# GitHub Actions пример
name: Clojure CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: DeLaGuardo/setup-clojure@10
with:
backend: 'cli'
version: '1.11.1'
- run: clj -M:test
- run: clj -M:lint
7. Екосистема от библиотеки
7.1 Web разработка
;; HTTP Сървър - Ring (чист Clojure)
{:deps {ring/ring {:mvn/version "1.9.5"}}
;; Routes - Compojure
{:deps {compojure/compojure {:mvn/version "1.6.2"}}}
7.2 Обработка на данни
;; Манипулация на данни
{:deps {zipkin/zikaron {:mvn/version "1.0.0"}}}
;; CSV handling
{:deps {org.clojure/data.csv {:mvn/version "1.0.1"}}
;; JSON (чист Clojure)
{:deps {org.clojure/data.json {:mvn/version "2.4.0"}}}
7.3 Асинхронно програмиране
;; Core async е вграден
;; Няма допълнителни зависимости за канали
;; За допълнителни async модели
{:deps {manifold/manifold {:mvn/version "0.4.0"}}}
7.4 Достъп до бази данни
;; Чист Clojure JDBC wrapper
{:deps {org.clojure/java.jdbc {:mvn/version "0.7.12"}}
;; SQL DSL
{:deps {sqlkorma/sqlkorma {:mvn/version "0.4.0"}}}
7.5 Библиотеки за тестване
{:deps {expectations/expectations {:mvn/version "2.0.0"}}
{midje/midje {:mvn/version "1.9.10"}}
{org.clojure/test.check {:mvn/version "1.1.1"}}}
7.6 Намиране на библиотеки
- Clojars.org - Общностно хранилище
- Clojuredocs.org - Документация с примери
- Awesome-clojure - Куриран списък
8. Техники за дебъгване
8.1 Print дебъгване
;; Прости отпечатвания
(println "Debug:" variable)
;; С pretty printer
(require '[clojure.pprint :as pp])
(pp/pprint data-structure)
;; Tap за дебъгване
(tap> {:event :processing :data x})
8.2 Stack traces
;; Вземете пълен stack trace
(.printStackTrace *e)
;; Ex-data за spec грешки
(try
(s/conform ::spec value)
(catch Exception e
(ex-data e)))
8.3 REPL дебъгване
;; Инспектиране на стойности
user> (def data (s/gen ::my-spec))
user> data
;; Стъпка по стъпка с trace
(trace (reduce + (range 10)))
8.4 Watching state
;; Watch atoms
(add-watch my-atom :debug
(fn [k r o n]
(println "Променено:" o "->" n)))
;; Watch refs в транзакция
(dosync
(trace (alter my-ref f)))
8.5 Breakpoints
;; Използване на dbg macro (от tools.trace)
(require '[clojure.tools.trace :as t])
(t/dbg expression)
8.6 Logging
(require '[clojure.tools.logging :as log])
(log/info "Приложението стартира")
(log/debug "Обработка" :item item)
(log/error e "Неуспех при обработка")
9. Индекс
A
B
build- 6.1
C
D
E
eastwood- 5.1
F
find-libs- 7.6
G
gen- 3.1
I
instrument- 3.1
L
M
N
ns-publics- 4.1
P
Q
quick-check- 3.1
R
S
T
U
V
verify- 3.1
Чист Clojure: Структура на проекта и инструменти