Files
dimgigov 23252826a0 feat: add Clojure/Nim chapter to the book + restructure for GitLab
- 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
2026-05-08 23:32:11 +03:00

14 KiB
Raw Permalink Blame History

Чист Clojure: Структура на проекта и инструменти

Съдържание

  1. Структура на проекта
  2. deps.edn и CLI инструменти
  3. Инфраструктура за тестване
  4. Работен процес за разработка
  5. Инструменти за качество на кода
  6. Изграждане и деплойване
  7. Екосистема от библиотеки
  8. Техники за дебъгване
  9. Индекс

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

C

  • check-ns - 5.4
  • clojure.tools.namespace - 4.1
  • comment - 4.4

D

E

  • eastwood - 5.1

F

  • find-libs - 7.6

G

I

  • instrument - 3.1

L

M

N

  • ns-publics - 4.1

P

  • pp/pprint - 8.1
  • profile - 8.3

Q

  • quick-check - 3.1

R

S

T

U

  • uberjar - 6.1
  • use-fixtures - 3.2

V


Чист Clojure: Структура на проекта и инструменти