5.0 KiB
5.0 KiB
Integreat Development Guide
Build & Run Commands
Build
lein build # Create uberjar
lein uberjar # Standalone JAR
npm install # Install Node.js dependencies (for frontend build)
Development
docker-compose up -d # Start Datomic, Solr services
lein repl # Start Clojure REPL (nREPL on port 9000), typically one will be running for you already
lein cljfmt check # Check code formatting
lein cljfmt fix # Auto-format code
clj-paren-repair [FILE ...] # fix parentheses in files
clj-nrepl-eval -p PORT "(+ 1 2 3)" # evaluate clojure code
Often times, if a file won't compile, first clj-paren-repair on the file, then try again. If it doesn't wor still, try cljfmt check.
Running the Application
INTEGREAT_JOB="" lein run # Default: port 3000
# Or with custom port:
PORT=3449 lein run
Test Execution
prefer using clojure-eval skill
Run All Tests
lein test # WORST CASE
Run Specific Test Selectors
lein test :integration # WORST CASE
lein test :functional #WORST CASE
Run Specific Test File
clj-nrepl-eval -p PORT "(clojure.test/run-tests 'auto-ap.import.plaid-test)" # preferred method
lein test auto-ap.import.plaid-test #WORST CASE
Run Specific Test Function
lein test auto-ap.import.plaid-test/plaid->transaction #WORST CASE
Code Style Guidelines
File Organization
- Namespaces follow
auto-ap.*format - Source files in
src/clj/auto_ap/ - Test files in
test/clj/auto_ap/ - Backend: Clojure 1.10.1
- Frontend: alpinejs + TailwindCSS + HTMX
Import Formatting
- Imports must be sorted alphabetically within each group
- Standard library imports first (
clojure.core,clojure.string, etc.) - Third-party libraries second
- Internal library imports last
- Group related imports together
Example:
(ns auto-ap.example
(:require
[auto-ap.datomic :refer [conn pull-many]]
[auto-ap.graphql.utils :refer [limited-clients]]
[clojure.data.json :as json]
[clojure.string :as str]
[com.brunobonacci.mulog :as mu]
[datomic.api :as dc]))
Naming Conventions
- Namespace names:
auto-ap.<feature>.<sub-feature> - File names: kebab-case (e.g.,
ledger_common.clj) - Functions: lowercase with underscores, descriptive (e.g.,
fetch-ids,process-invoice) - Database identifiers: use
:db/idkeyword, not string IDs - Entity attributes: follow schema convention (e.g.,
:invoice/status)
Data Types & Schema
- Use keywords for schema accessors (e.g.,
:invoice/dateinstead of strings) - Datomic pull queries use
[:db/id ...]syntax - Use
:db/identfor schema keyword resolution - Dates and times: use
insttypes (handled byclj-time.coerce) - Numbers: use
doublefor monetary values (stored as strings in DB, converted to double)
Error Handling
- Use
slingshot.slingshotlibrary for error handling - Pattern:
(exception->notification #(throw ...))to wrap errors with logging - Pattern:
(notify-if-locked client-id invoice-date)for business logic checks - Throw
ex-infowith metadata for structured exceptions - Use
throw+for throwing exceptions that can be caught byslingshot
Example:
(exception->notification
(when-not (= :invoice-status/unpaid (:invoice/status invoice))
(throw (ex-info "Cannot void an invoice if it is paid."
{:type :notification}))))
Function Definitions
- Use
defnfor public functions - Use
defn-for private functions (indicated by underscore prefix) - Use
letfnfor locally recursive functions - Use
condandcondpfor conditional logic - Use
casefor exhaustive keyword matching
Testing Patterns
- Use
clojure.testwithdeftestandtesting - Group related tests in
testingblocks - Use
isfor assertions with descriptive messages - Use
arefor parameterized assertions - Fixtures with
use-fixturesfor setup/teardown
Example:
(deftest import-invoices
(testing "Should import valid invoice"
(is (= 1 (invoice-count-for-client [:client/code "ABC"]))))
(testing "Should not import duplicate invoice"
(is (thrown? Exception (sut/import-uploaded-invoice user invoices)))))
look at the testing-conventions skill for more detail
Code Formatting & Documentation
- Use
lein cljfmt checkbefore committing, auto-fix withlein cljfmt fix - Use
#_for commenting out entire forms (not single lines) - Use
commentspecial form for evaluation examples - Use
#_to comment out library dependencies in project.clj
Logging
- Uses
com.brunobonacci.mulogfor structured logging - Import with
(require [auto-ap.logging :as alog]) - Use
alog/info,alog/warn,alog/errorfor different log levels - Use
alog/with-context-asto add context to log messages
Linting Configuration
- Uses
clj-kondofor static analysis - Custom linters configured in
.clj-kondo/config.edn - Hooks for
mount.core/defstateandauto-ap.loggingin.clj-kondo/hooks/ - Run
clj-kondo --lint src/clj auto_ap/for linting