Skip to content

Commit

Permalink
Introduce "boundary" layer (lein)
Browse files Browse the repository at this point in the history
  • Loading branch information
lagenorhynque committed Dec 23, 2019
1 parent c50ed39 commit 4eae1b8
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 41 deletions.
6 changes: 6 additions & 0 deletions minimal-api-lein/dev/src/dev.clj
Expand Up @@ -20,3 +20,9 @@
(defn test
([] (test/run-all-tests #"minimal-api-lein\..+-test"))
([ns-sym] (test/run-tests ns-sym)))

(defn db []
(:minimal-api-lein.boundary.db.core/db system))

(defn db-run [f & args]
(apply f (db) args))
6 changes: 4 additions & 2 deletions minimal-api-lein/resources/config.edn
@@ -1,5 +1,7 @@
{:minimal-api-lein.routes/routes {}
:minimal-api-lein.server/app {:routes #ig/ref :minimal-api-lein.routes/routes}
{:minimal-api-lein.boundary.db.core/db {}
:minimal-api-lein.routes/routes {}
:minimal-api-lein.server/app {:routes #ig/ref :minimal-api-lein.routes/routes
:db #ig/ref :minimal-api-lein.boundary.db.core/db}
:minimal-api-lein.server/server {:app #ig/ref :minimal-api-lein.server/app
:options {:port 3000
:join? false}}}
35 changes: 35 additions & 0 deletions minimal-api-lein/src/minimal_api_lein/boundary/db/core.clj
@@ -0,0 +1,35 @@
(ns minimal-api-lein.boundary.db.core
(:require [clojure.string :as str]
[integrant.core :as ig]))

(defrecord Boundary [data])

(defmethod ig/init-key ::db [_ _]
(->Boundary (atom {:todo {"todo1" {"task" "build an API"}
"todo2" {"task" "?????"}
"todo3" {"task" "profit!"}}})))

(defn select-all [{:keys [data]} table]
(get @data table))

(defn selet-by-key [{:keys [data]} table k]
(get-in @data [table k]))

(defn insert! [{:keys [data]} table key-prefix v]
(let [id (->> (keys (get @data table))
(map #(-> %
(str/replace-first key-prefix "")
Long/parseLong))
(apply max)
inc)
k (str key-prefix id)]
(swap! data assoc-in [table k] v)
k))

(defn update-by-key! [{:keys [data]} table k v]
(swap! data assoc-in [table k] v)
nil)

(defn delete-by-key! [{:keys [data]} table k]
(swap! data update table dissoc k)
nil)
22 changes: 22 additions & 0 deletions minimal-api-lein/src/minimal_api_lein/boundary/db/todo.clj
@@ -0,0 +1,22 @@
(ns minimal-api-lein.boundary.db.todo
(:require [minimal-api-lein.boundary.db.core :as db]))

(defprotocol Todo
(find-todos [db])
(find-todo-by-id [db id])
(create-todo! [db todo])
(update-todo! [db id todo])
(delete-todo! [db id]))

(extend-protocol Todo
minimal_api_lein.boundary.db.core.Boundary
(find-todos [db]
(db/select-all db :todo))
(find-todo-by-id [db id]
(db/selet-by-key db :todo id))
(create-todo! [db todo]
(db/insert! db :todo "todo" todo))
(update-todo! [db id todo]
(db/update-by-key! db :todo id todo))
(delete-todo! [db id]
(db/delete-by-key! db :todo id)))
6 changes: 0 additions & 6 deletions minimal-api-lein/src/minimal_api_lein/db.clj

This file was deleted.

35 changes: 14 additions & 21 deletions minimal-api-lein/src/minimal_api_lein/handler.clj
@@ -1,34 +1,27 @@
(ns minimal-api-lein.handler
(:require [clojure.string :as str]
[minimal-api-lein.db :refer [todos]]
(:require [minimal-api-lein.boundary.db.todo :as db.todo]
[ring.util.http-response :as response]))

(defn list-todos [_]
(response/ok @todos))
(defn list-todos [{:keys [db]}]
(response/ok (db.todo/find-todos db)))

(defn create-todo [{:keys [params]}]
(let [id (->> (keys @todos)
(map #(-> %
(str/replace-first "todo" "")
Long/parseLong))
(apply max)
inc)
todo-id (str "todo" id)]
(swap! todos assoc todo-id {"task" (:task params)})
(response/created (str "/todos/" todo-id) (get @todos todo-id))))
(defn create-todo [{:keys [params db]}]
(let [id (db.todo/create-todo! db {"task" (:task params)})
todo (db.todo/find-todo-by-id db id)]
(response/created (str "/todos/" id) todo)))

(defn fetch-todo [{:keys [params]}]
(if-let [todo (get @todos (:todo-id params))]
(defn fetch-todo [{:keys [params db]}]
(if-let [todo (db.todo/find-todo-by-id db (:todo-id params))]
(response/ok todo)
(response/not-found {:message (str "Todo " (:todo-id params) " doesn't exist")})))

(defn delete-todo [{:keys [params]}]
(if (get @todos (:todo-id params))
(do (swap! todos dissoc (:todo-id params))
(defn delete-todo [{:keys [params db]}]
(if (db.todo/find-todo-by-id db (:todo-id params))
(do (db.todo/delete-todo! db (:todo-id params))
(response/no-content))
(response/not-found {:message (str "Todo " (:todo-id params) " doesn't exist")})))

(defn update-todo [{:keys [params]}]
(defn update-todo [{:keys [params db]}]
(let [task {"task" (:task params)}]
(swap! todos assoc (:todo-id params) task)
(db.todo/update-todo! db (:todo-id params) task)
(response/created (str "/todos/" (:todo-id params)) task)))
4 changes: 4 additions & 0 deletions minimal-api-lein/src/minimal_api_lein/middleware.clj
Expand Up @@ -8,3 +8,7 @@
(update :params (partial transform-keys #(->kebab-case % :separator \_)))
handler)]
(transform-keys #(->snake_case % :separator \-) response))))

(defn wrap-db [handler db]
(fn [request]
(handler (assoc request :db db))))
7 changes: 4 additions & 3 deletions minimal-api-lein/src/minimal_api_lein/server.clj
@@ -1,19 +1,20 @@
(ns minimal-api-lein.server
(:require [bidi.ring :refer [make-handler]]
[integrant.core :as ig]
[minimal-api-lein.middleware :refer [wrap-kebab-case-keys]]
[minimal-api-lein.middleware :refer [wrap-db wrap-kebab-case-keys]]
[ring.adapter.jetty :as jetty]
[ring.middleware.json :refer [wrap-json-params wrap-json-response]]
[ring.middleware.keyword-params :refer [wrap-keyword-params]]
[ring.middleware.params :refer [wrap-params]]))

(defmethod ig/init-key ::app [_ {:keys [routes]}]
(defmethod ig/init-key ::app [_ {:keys [routes db]}]
(-> (make-handler routes)
wrap-kebab-case-keys
wrap-keyword-params
wrap-json-params
wrap-json-response
wrap-params))
wrap-params
(wrap-db db)))

(defmethod ig/init-key ::server [_ {:keys [app options]}]
(jetty/run-jetty app options))
Expand Down
6 changes: 3 additions & 3 deletions minimal-api-lein/test/minimal_api_lein/core_test.clj
Expand Up @@ -5,9 +5,9 @@

(t/deftest test-api
(with-system [sys (helper/test-system)]
(with-db-data {"todo1" {"task" "build an API"}
"todo2" {"task" "?????"}
"todo3" {"task" "profit!"}}
(with-db-data [sys {:todo {"todo1" {"task" "build an API"}
"todo2" {"task" "?????"}
"todo3" {"task" "profit!"}}}]
(t/testing "TODOリストの一覧が取得できる"
(let [{:keys [status body]} (helper/http-get "/todos")]
(t/is (= 200 status))
Expand Down
12 changes: 6 additions & 6 deletions minimal-api-lein/test/minimal_api_lein/test_helper.clj
@@ -1,8 +1,7 @@
(ns minimal-api-lein.test-helper
(:require [clj-http.client :as client]
[clojure.java.io :as io]
[integrant.core :as ig]
[minimal-api-lein.db :as db]))
[integrant.core :as ig]))

(defn test-config []
(-> (io/resource "config.edn")
Expand All @@ -24,12 +23,13 @@
~@body
(finally (ig/halt! ~bound-sym)))))

(defmacro with-db-data [db-data-map & body]
`(let [old-val# @db/todos]
(defmacro with-db-data [[system db-data-map] & body]
`(let [db# (:minimal-api-lein.boundary.db.core/db ~system)
old-val# @(:data db#)]
(try
(reset! db/todos ~db-data-map)
(reset! (:data db#) ~db-data-map)
~@body
(finally (reset! db/todos old-val#)))))
(finally (reset! (:data db#) old-val#)))))

;;; HTTP client

Expand Down

0 comments on commit 4eae1b8

Please sign in to comment.