Exemples de Langage Clojure

Exemples essentiels de programmation Clojure pour la programmation fonctionnelle sur la JVM

💻 Clojure Hello World clojure

🟢 simple ⭐⭐

Programme Hello World de base en Clojure et concepts fondamentaux de programmation fonctionnelle style Lisp

⏱️ 20 min 🏷️ clojure, functional, lisp, jvm, syntax
Prerequisites: Basic programming concepts, Understanding of functional programming
;; Clojure Hello World Examples

;; 1. Basic Hello World
(println "Hello, World!")

;; 2. Hello World with function definition
(defn hello-world []
  (println "Hello, World!"))

(hello-world)

;; 3. Hello World with parameters
(defn greet [name]
  (println (str "Hello, " name "!")))

(greet "Clojure")
(greet "World")

;; 4. Hello World with multiple arities
(defn multi-greet
  ([] (println "Hello, World!"))
  ([name] (println (str "Hello, " name "!")))
  ([greeting name] (println (str greeting ", " name "!"))))

(multi-greet)
(multi-greet "Clojure")
(multi-greet "Good morning" "Clojure")

;; 5. Hello World with anonymous function
(let [greet-fn (fn [name] (println (str "Hello, " name "!")))]
  (greet-fn "Anonymous"))

;; 6. Hello World with higher-order function
(defn greet-twice [greet-fn name]
  (greet-fn name)
  (greet-fn name))

(greet-twice greet "Twice")

;; Basic data types and operations

;; Numbers
(def num1 42)
(def num2 3.14)
(def num3 2/3)  ; Rational number

(println "Integer:" num1)
(println "Float:" num2)
(println "Rational:" num3)
(println "Addition:" (+ num1 num2))
(println "Multiplication:" (* num1 2))

;; Strings
(def text "Clojure Programming")
(println "String:" text)
(println "Length:" (count text))
(println "Uppercase:" (clojure.string/upper-case text))
(println "Substring:" (subs text 0 6))

;; Keywords (efficient identifiers)
(def status :active)
(def user {:name "Alice" :age 30 :status status})
(println "User:" user)
(println "User name:" (:name user))
(println "User age:" (:age user))

;; Vectors (ordered collections)
(def numbers [1 2 3 4 5])
(println "Vector:" numbers)
(println "First element:" (first numbers))
(println "Last element:" (last numbers))
(println "Rest:" (rest numbers))
(println "Conj:" (conj numbers 6))

;; Maps (key-value collections)
(def person {
  :name "Bob"
  :age 25
  :city "New York"
  :hobbies ["reading" "coding" "music"]
})

(println "Person:" person)
(println "Person name:" (:name person))
(println "Person hobbies:" (:hobbies person))

;; Sets (unique collections)
(def unique-numbers #{1 2 3 4 5})
(println "Set:" unique-numbers)
(println "Contains 3?" (contains? unique-numbers 3))
(println "Add element:" (conj unique-numbers 6))

;; Lists (linked lists)
(def list-data '(1 2 3 4 5))
(println "List:" list-data)
(println "First:" (first list-data))
(println "Rest:" (rest list-data))

;; Basic functions

;; Function with multiple return values (using vector)
(defn divide-and-remainder [dividend divisor]
  (let [quotient (quot dividend divisor)
        remainder (rem dividend divisor)]
    [quotient remainder]))

(let [[q r] (divide-and-remainder 17 5)]
  (println "17 ÷ 5 =" q "remainder" r))

;; Conditional logic
(defn describe-number [n]
  (cond
    (< n 0) "negative"
    (= n 0) "zero"
    (< n 10) "small positive"
    :else "large positive"))

(println "-5 is" (describe-number -5))
(println "0 is" (describe-number 0))
(println "5 is" (describe-number 5))
(println "15 is" (describe-number 15))

;; Pattern matching with case
(defn describe-value [x]
  (case x
    1 "one"
    2 "two"
    3 "three"
    "hello" "greeting"
    :keyword "a keyword"
    "unknown value"))

(println "Value of 1:" (describe-value 1))
(println "Value of 'hello':" (describe-value "hello"))

;; Recursion
(defn factorial [n]
  (if (<= n 1)
    1
    (* n (factorial (dec n)))))

(println "5! =" (factorial 5))

;; Tail recursion optimization
(defn factorial-tail [n acc]
  (if (<= n 1)
    acc
    (recur (dec n) (* n acc))))

(defn factorial [n]
  (factorial-tail n 1))

(println "10! =" (factorial 10))

;; Loop/recur for iteration
(defn sum-to [n]
  (loop [i 1 total 0]
    (if (> i n)
      total
      (recur (inc i) (+ total i)))))

(println "Sum of 1 to 100:" (sum-to 100))

;; Higher-order functions

;; Map - transform each element
(def doubled (map #(* % 2) [1 2 3 4 5]))
(println "Doubled:" doubled)

;; Filter - select elements
(def evens (filter even? [1 2 3 4 5 6]))
(println "Even numbers:" evens)

;; Reduce - aggregate elements
(def sum (reduce + [1 2 3 4 5]))
(println "Sum:" sum)

;; Function composition
(defn add-and-double [x]
  (-> x
      (+ 5)
      (* 2)))

(println "Add 5 and double 10:" (add-and-double 10))

;; Destructuring
(defn process-person [{:keys [name age city] :as person}]
  (println (str name " is " age " years old and lives in " city))
  (println "Full person data:" person))

(process-person {:name "Charlie" :age 35 :city "Boston"})

;; Vector destructuring
(defn process-coordinates [[x y z]]
  (println (str "X: " x ", Y: " y ", Z: " z)))

(process-coordinates [10 20 30])

;; Lazy sequences
(def naturals (iterate inc 1))
(def first-10 (take 10 naturals))
(println "First 10 natural numbers:" first-10)

(def even-numbers (filter even? naturals))
(def first-5-evens (take 5 even-numbers))
(println "First 5 even numbers:" first-5-evens)

;; Interoperability with Java
(import 'java.util.Date)
(import 'java.text.SimpleDateFormat)

(defn format-date [date]
  (let [formatter (SimpleDateFormat. "yyyy-MM-dd HH:mm:ss")]
    (.format formatter date)))

(def now (Date.))
(println "Current time:" (format-date now))

;; Using Java collections
(def java-list (java.util.ArrayList.))
(.add java-list "Java element")
(.add java-list "Another element")
(println "Java list:" (vec java-list))

;; Namespaces and requires (in real usage)
;; (ns my-app.core
;;   (:require [clojure.string :as str]
;;             [clojure.set :as set]))

;; Exception handling
(defn safe-divide [a b]
  (try
    (/ a b)
    (catch ArithmeticException e
      (println "Division by zero error!")
      nil)
    (finally
      (println "Division operation completed"))))

(println "10 ÷ 2 =" (safe-divide 10 2))
(println "10 ÷ 0 =" (safe-divide 10 0))

;; Atom for state management
(def counter (atom 0))

(defn increment-counter []
  (swap! counter inc))

(defn get-counter []
  @counter)

(println "Initial counter:" @counter)
(increment-counter)
(increment-counter)
(println "Final counter:" @counter)

;; Basic meta-programming
(defmacro debug [x]
  (let [result# ~x]
     (println (str '~x " = " result#))
     result#))

(def a 10)
(def b 20)
(debug (+ a b))

💻 Patrons de Programmation Fonctionnelle Clojure clojure

🟡 intermediate ⭐⭐⭐⭐

Techniques avancées de programmation fonctionnelle, structures de données immutables et patrons communs Clojure

⏱️ 30 min 🏷️ clojure, functional, patterns, immutable, protocols
Prerequisites: Clojure basics, Functional programming concepts, Data structures
;; Clojure Functional Programming Patterns

;; 1. Pure Functions and Immutability

;; Pure function - no side effects
(defn calculate-discount [price discount-rate]
  (* price (- 1 discount-rate)))

(println "Discounted price:" (calculate-discount 100 0.2))

;; Always return new data structures instead of modifying
(defn add-item-to-cart [cart item]
  (conj cart item))

(def original-cart [:book :pen])
(def new-cart (add-item-to-cart original-cart :notebook))
(println "Original cart:" original-cart)  ; Unchanged
(println "New cart:" new-cart)            ; New cart with added item

;; 2. Function Composition and Transducers

;; Function composition
(defn transform-data [data]
  (->> data
       (map #(* % 2))           ; Double each number
       (filter even?)            ; Keep only even numbers
       (take 5)                  ; Take first 5
       (reduce +)))              ; Sum them up

(def numbers (range 1 20))
(println "Transform result:" (transform-data numbers))

;; Transducers - composable algorithmic transformations
(def xf-map-double (map #(* % 2)))
(def xf-filter-evens (filter even?))
(def xf-take-five (take 5))

(def process-numbers
  (comp xf-map-double xf-filter-evens xf-take-five))

(println "Transducer result:" (into [] process-numbers numbers))

;; 3. Lazy Sequences and Infinite Data

;; Infinite sequence of Fibonacci numbers
(def fib-seq
  (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))

(def first-10-fib (take 10 fib-seq))
(println "First 10 Fibonacci numbers:" first-10-fib)

;; Lazy prime numbers
(defn prime? [n]
  (and (> n 1)
       (not-any? #(zero? (rem n %)) (range 2 (Math/sqrt n)))))

(def primes (filter prime? (iterate inc 2)))
(def first-10-primes (take 10 primes))
(println "First 10 prime numbers:" first-10-primes)

;; 4. Higher-Order Functions and Closures

;; Function factory - creates functions with captured values
(defn make-adder [increment]
  (fn [x] (+ x increment)))

(def add-5 (make-adder 5))
(def add-10 (make-adder 10))
(println "Add 5 to 10:" (add-5 10))
(println "Add 10 to 10:" (add-10 10))

;; Predicate factory
(defn make-predicate [operator value]
  (fn [x] (operator x value)))

(def greater-than-5 (make-predicate > 5))
(def less-than-10 (make-predicate < 10))
(println "Numbers >5:" (filter greater-than-5 [1 3 6 8 2 10]))
(println "Numbers <10:" (filter less-than-10 [1 3 6 8 2 10]))

;; 5. Data Transformation Pipelines

;; Pipeline for processing user data
(defn process-user-data [raw-users]
  (->> raw-users
       ;; Remove inactive users
       (filter #(:active %))
       ;; Calculate age group
       (map #(assoc % :age-group
                    (cond
                      (< (:age %) 18) "minor"
                      (< (:age %) 65) "adult"
                      :else "senior")))
       ;; Sort by age
       (sort-by :age)
       ;; Extract only relevant fields
       (map #(select-keys % [:name :age :age-group]))))

(def raw-users [
  {:name "Alice" :age 25 :active true :email "[email protected]"}
  {:name "Bob" :age 17 :active true :email "[email protected]"}
  {:name "Charlie" :age 70 :active false :email "[email protected]"}
  {:name "Diana" :age 30 :active true :email "[email protected]"}])

(println "Processed users:" (process-user-data raw-users))

;; 6. Recursive Patterns and Tree Processing

;; Generic tree processing
(defn process-tree [node f-leaf f-node]
  (if (:children node)
    (f-node (:value node)
            (map #(process-tree % f-leaf f-node) (:children node)))
    (f-leaf (:value node))))

(def sample-tree
  {:value "root"
   :children [
    {:value "left" :children [
      {:value "left-left"}
      {:value "left-right"}
    ]}
    {:value "right" :children [
      {:value "right-left"}
    ]}
  ]})

(defn sum-leaves [x]
  (if (number? x) x 0))

(defn sum-node [value child-sums]
  (+ (sum-leaves value) (reduce + child-sums)))

(def numeric-tree
  {:value 1
   :children [
    {:value 2 :children [{:value 3} {:value 4}]}
    {:value 5 :children [{:value 6}]}
  ]})

(println "Tree sum:" (process-tree numeric-tree sum-leaves sum-node))

;; 7. Memoization and Caching

;; Memoized expensive function
(defn slow-fibonacci [n]
  (if (< n 2)
    n
    (+ (slow-fibonacci (dec n)) (slow-fibonacci (- n 2)))))

(def memo-fibonacci (memoize slow-fibonacci))

(println "Memoized fib 40:" (memo-fibonacci 40))  ; Much faster

;; Cache with TTL (time-to-live)
(defn create-ttl-cache [ttl-ms]
  (let [cache (atom {})]
    (fn [key f]
      (let [now (System/currentTimeMillis)
            cached-value (get @cache key)]
        (if (and cached-value (< (- now (:timestamp cached-value)) ttl-ms))
          (:value cached-value)
          (let [new-value (f)]
            (swap! cache assoc key {:value new-value :timestamp now})
            new-value))))))

(def expensive-cache (create-ttl-cache 5000))  ; 5 second TTL

(defn expensive-calculation [x]
  (println "Performing expensive calculation for" x)
  (* x x x))

(println "Cached result:" (expensive-cache 10 expensive-calculation))
(println "Cached result again:" (expensive-cache 10 expensive-calculation))

;; 8. State Management Patterns

;; Event sourcing pattern
(defn add-event [state event]
  (case (:type event)
    :add-item (update state :items conj (:item event))
    :remove-item (update state :items disj (:item event))
    :update-quantity (assoc-in state [:quantities (:item event)] (:quantity event))
    state))

(def initial-state {:items #{} :quantities {}})

(def events [
  {:type :add-item :item :book}
  {:type :add-item :item :pen}
  {:type :update-quantity :item :book :quantity 2}
  {:type :remove-item :item :pen}
])

(def final-state (reduce add-event initial-state events))
(println "Final state:" final-state)

;; 9. Protocol and Multimethod Patterns

;; Multimethods for polymorphic behavior
(defmulti area :shape)

(defmethod area :circle [{:keys [radius]}]
  (* Math/PI radius radius))

(defmethod area :rectangle [{:keys [width height]}]
  (* width height))

(defmethod area :triangle [{:keys [base height]}]
  (* 0.5 base height))

(def shapes [
  {:shape :circle :radius 5}
  {:shape :rectangle :width 4 :height 6}
  {:shape :triangle :base 3 :height 8}
])

(println "Areas:" (map area shapes))

;; Protocols for interface-like behavior
(defprotocol Drawable
  (draw [this]))

(defrecord Circle [radius]
  Drawable
  (draw [this]
    (println (str "Drawing circle with radius " radius))))

(defrecord Rectangle [width height]
  Drawable
  (draw [this]
    (println (str "Drawing rectangle " width "x" height))))

(def shapes-objects [(->Circle 10) (->Rectangle 5 8)])
(dorun (map draw shapes-objects))

;; 10. Error Handling and Validation

;; Result pattern with explicit success/failure
(defn safe-divide [a b]
  (if (zero? b)
    {:success false :error "Division by zero"}
    {:success true :result (/ a b)}))

(defn chain-operations [initial operations]
  (reduce (fn [acc op]
            (if (:success acc)
              (op (:result acc))
              acc))
          initial
          operations))

(def calculation-pipeline [
  #(safe-divide % 2)    ; Divide by 2
  #(safe-divide % 0)    ; This will fail
  #(safe-divide % 3)])  ; This won't be reached

(println "Calculation result:" (chain-operations 100 calculation-pipeline))

;; Validation with accumulation
(defn validate-user [user]
  (let [errors (cond-> []
                 (empty? (:name user)) (conj "Name is required")
                 (not (:email user)) (conj "Email is required")
                 (and (:age user) (< (:age user) 0)) (conj "Age must be non-negative"))]
    (if (empty? errors)
      {:success true :user user}
      {:success false :errors errors})))

(def test-users [
  {:name "Alice" :email "[email protected]" :age 25}
  {:name "" :email "[email protected]" :age -5}
  {}])

(println "Validation results:" (map validate-user test-users))

💻 Développement Web et Macros Clojure clojure

🔴 complex ⭐⭐⭐⭐⭐

Développement web avec Ring/Compojure et programmation de macros avancée pour métaprogrammation

⏱️ 45 min 🏷️ clojure, web, macros, ring, compojure, api
Prerequisites: Advanced Clojure, Web concepts, Database knowledge, Macro understanding
;; Clojure Web Development and Advanced Macros

;; 1. Ring and Compojure Web Framework Basics

;; Basic Ring handler
(defn hello-handler [request]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body "<h1>Hello from Clojure!</h1>"})

;; Route-based handler with Compojure
(require '[compojure.core :refer [defroutes GET POST]])
(require '[ring.adapter.jetty :as jetty])

(defroutes app-routes
  (GET "/" [] "Welcome to the homepage!")
  (GET "/hello/:name" [name] (str "Hello, " name "!"))
  (GET "/api/users" []
    {:status 200
     :headers {"Content-Type" "application/json"}
     :body "[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]"})
  (POST "/api/users" [name email]
    {:status 201
     :headers {"Content-Type" "application/json"}
     :body (str "{"id":3,"name":"" name "","email":"" email ""}")})
  (GET "/files/:filename" [filename]
    (java.io.File. "public/" filename)))

;; Middleware for logging
(defn wrap-logging [handler]
  (fn [request]
    (println (str "Processing " (:request-method request) " " (:uri request)))
    (let [response (handler request)]
      (println (str "Response status: " (:status response)))
      response)))

;; Middleware for JSON responses
(defn wrap-json [handler]
  (fn [request]
    (let [response (handler request)]
      (if (map? (:body response))
        (assoc-in response [:headers "Content-Type"] "application/json")
        response))))

;; Middleware stack
(def app
  (-> app-routes
      wrap-logging
      wrap-json))

;; 2. Advanced Macro Programming

;; Macro for route definition with automatic documentation
(defmacro defpage [route description & body]
  (do
     (defn ~(symbol (str "page-" (hash route))) [request#]
       ~@body)
     (alter-var-root #'*routes* conj {:path ~route :description ~description})
     ~(symbol (str "page-" (hash route)))))

(def *routes* (atom []))

(defpage "/about" "About page"
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body "<h1>About Us</h1><p>We are a Clojure web application!</p>"})

;; Debugging macro with expression information
(defmacro dbg [x]
  (let [result# ~x]
     (println (str "Debug: " '~x " = " result#))
     result#))

;; Time execution macro
(defmacro time-execution [& body]
  (let [start# (System/nanoTime)
         result# (do ~@body)
         end# (System/nanoTime)
         duration# (/ (- end# start#) 1000000.0)]
     (println (str "Execution time: " duration# " ms"))
     result#))

;; Usage example:
;; (time-execution
;;   (Thread/sleep 100)
;;   (println "Done"))

;; Conditional compilation macro
(defmacro when-dev [& body]
  (when (= "development" (or (System/getenv "ENV") "development"))
     ~@body))

(when-dev
  (println "Development mode enabled"))

;; 3. Database Operations and Transactions

(require '[clojure.java.jdbc :as jdbc])

(def db-spec {:subprotocol "h2"
              :subname "mem:example"
              :user "sa"
              :password ""})

;; Table creation
(defn create-users-table! []
  (jdbc/db-do-commands db-spec
    [(jdbc/create-table-ddl :users
       [[:id :int "PRIMARY KEY"]
        [:name "VARCHAR(50)"]
        [:email "VARCHAR(100)"]
        [:created_at :timestamp]])]))

;; CRUD operations
(defn create-user! [user-data]
  (jdbc/insert! db-spec :users
    (assoc user-data :created_at (java.sql.Timestamp. (System/currentTimeMillis)))))

(defn get-user [id]
  (first (jdbc/query db-spec ["SELECT * FROM users WHERE id = ?" id])))

(defn update-user! [id user-data]
  (jdbc/update! db-spec :users user-data ["id = ?" id]))

(defn delete-user! [id]
  (jdbc/delete! db-spec :users ["id = ?" id]))

(defn list-users []
  (jdbc/query db-spec ["SELECT * FROM users ORDER BY created_at DESC"]))

;; Transaction example
(defn transfer-users! [from-id to-id]
  (jdbc/with-db-transaction [t-conn db-spec]
    (let [from-user (first (jdbc/query t-conn ["SELECT * FROM users WHERE id = ?" from-id]))
          to-user (first (jdbc/query t-conn ["SELECT * FROM users WHERE id = ?" to-id]))]
      (when (and from-user to-user)
        ;; Update both users in a single transaction
        (jdbc/update! t-conn :users
          {:name (str (:name from-user) "_transferred")}
          ["id = ?" from-id])
        (jdbc/update! t-conn :users
          {:name (str (:name to-user) "_received")}
          ["id = ?" to-id])
        true))))

;; 4. Authentication and Authorization

;; Password hashing
(require '[buddy.hashers :as hashers])

(defn hash-password [password]
  (hashers/derive password {:algorithm :bcrypt}))

(defn verify-password [password hashed-password]
  (hashers/verify password hashed-password))

;; JWT token generation (requires buddy-sign)
(require '[buddy.sign.jwt :as jwt])

(def secret-key "your-secret-key-here")

(defn generate-token [user-id]
  (jwt/sign {:user-id user-id :exp (+ (System/currentTimeMillis) 3600000)} secret-key))

(defn verify-token [token]
  (try
    (jwt/unsign token secret-key)
    (catch Exception e
      {:error "Invalid token"})))

;; Authentication middleware
(defn wrap-authentication [handler]
  (fn [request]
    (let [token (get-in request [:headers "authorization"])
          auth-result (when token (verify-token token))]
      (if (:error auth-result)
        {:status 401 :body {:error "Unauthorized"}}
        (handler (assoc request :user (:data auth-result)))))))

;; Authorization macro
(defmacro require-role [role & body]
  (let [user# (:user *request*)]
     (if (= (:role user#) ~role)
       (do ~@body)
       {:status 403 :body {:error "Forbidden"}})))

;; 5. RESTful API with Data Validation

;; Validation library usage
(require '[clojure.spec.alpha :as s])

(s/def ::name string?)
(s/def ::email #(re-matches #".+@.+..+" %))
(s/def ::age int?)
(s/def ::user (s/keys :req-un [::name ::email]
                    :opt-un [::age]))

(defn validate-request [data spec]
  (if (s/valid? spec data)
    {:success true :data data}
    {:success false :errors (s/explain-data spec data)}))

;; API endpoint with validation
(defn create-user-api [request]
  (let [user-data (:body request)
        validation (validate-request user-data ::user)]
    (if (:success validation)
      (try
        (let [created-user (create-user! (:data validation))]
          {:status 201 :body created-user})
        (catch Exception e
          {:status 500 :body {:error "Database error"}}))
      {:status 400 :body {:errors (:errors validation)}})))

;; 6. Real-time WebSocket Communication

(require '[ring.adapter.jetty :as jetty])
(require '[clojure.data.json :as json])

(def websocket-connections (atom #{}))

(defn websocket-handler [request]
  {:status 101
   :headers {"Upgrade" "websocket"
            "Connection" "Upgrade"}})

(defn broadcast-message! [message]
  (doseq [connection @websocket-connections]
    (try
      (.send connection (json/write-str message))
      (catch Exception e
        (swap! websocket-connections disj connection)))))

(defn handle-websocket-message [connection message]
  (let [parsed (json/read-str message)]
    (case (:type parsed)
      :chat (broadcast-message!
             {:type :chat
              :user (:user parsed)
              :message (:message parsed)
              :timestamp (System/currentTimeMillis)})
      :join (swap! websocket-connections conj connection))))

;; 7. Background Jobs and Task Queue

;; Simple task queue using atoms
(def task-queue (atom clojure.lang.PersistentQueue/EMPTY))
(def workers (atom {}))

(defn enqueue-task! [task]
  (swap! task-queue conj task))

(defn dequeue-task! []
  (first (swap-vals! task-queue pop)))

(defn start-worker! [worker-id]
  (future
    (loop []
      (when-let [task (dequeue-task!)]
        (try
          (println (str "Worker " worker-id " processing task: " (:type task)))
          ((:handler task) task)
          (catch Exception e
            (println (str "Worker " worker-id " error: " (.getMessage e)))))
        (recur)))))

(defn stop-worker! [worker-id]
  (when-let [worker-future (get @workers worker-id)]
    (future-cancel worker-future)
    (swap! workers dissoc worker-id)))

;; Example tasks
(defn send-email-task [task]
  (Thread/sleep 2000)  ; Simulate sending email
  (println (str "Email sent to " (:to task))))

(defn process-image-task [task]
  (Thread/sleep 5000)  ; Simulate image processing
  (println (str "Image processed: " (:image-path task))))

;; Enqueue tasks
(enqueue-task! {:type :send-email
                :to "[email protected]"
                :handler send-email-task})

(enqueue-task! {:type :process-image
                :image-path "/path/to/image.jpg"
                :handler process-image-task})

;; Start worker threads
(swap! workers assoc :worker1 (start-worker! 1))
(swap! workers assoc :worker2 (start-worker! 2))

;; 8. Configuration Management

;; Configuration loading
(defn load-config [env]
  (case env
    :development {:port 3000 :db-url "jdbc:h2:mem:dev" :debug true}
    :test {:port 3001 :db-url "jdbc:h2:mem:test" :debug false}
    :production {:port 80 :db-url "jdbc:postgresql://prod/db" :debug false}
    {:port 3000 :db-url "jdbc:h2:mem:default" :debug false}))

(def config (load-config (keyword (or (System/getenv "ENV") "development"))))

;; Environment-specific middleware
(defn wrap-dev-only [handler]
  (if (:debug config)
    handler
    (fn [request] {:status 404 :body "Not found"})))

;; 9. Error Handling and Logging

;; Structured logging
(defn log-event [level message data]
  (let [log-entry {:timestamp (System/currentTimeMillis)
                   :level level
                   :message message
                   :data data}]
    (println (clojure.data.json/write-str log-entry))
    log-entry))

;; Global error handler
(defn wrap-error-handling [handler]
  (fn [request]
    (try
      (handler request)
      (catch Exception e
        (log-event :error "Unhandled exception"
                   {:exception (.getMessage e)
                    :stack-trace (map str (.getStackTrace e))})
        {:status 500 :body {:error "Internal server error"}}))))

;; 10. Testing and Development Tools

;; Test data generation
(defn generate-test-user []
  {:name (str "Test User " (rand-int 1000))
   :email (str "test" (rand-int 1000) "@example.com")
   :age (+ 18 (rand-int 50))})

;; Route testing helper
(defn test-route [app-routes request-method uri]
  ((app-routes {:request-method request-method :uri uri}) {}))

;; Start the server (comment out in production)
;; (jetty/run-jetty app {:port (:port config) :join? false})