Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement and recommend a DRYer approach #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 32 additions & 15 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,37 @@ Please see [breaking changes](/changes.markdown#0.1.7) in 0.1.7.
(:require [com.grzm.component.pedestal :as cp]))
```

* Define all the components which you intend to use from Pedestal into a fixed vector: `(def components-to-inject [:db :sms :worker])`

Pedestal Component provides a constructor that takes a single
0-arity function which returns the Pedestal configuration service map.
The service map is passed to `io.pedestal.http/create-server`.

* Use said constructor, along with `components-to-inject` as dependencies:

```clojure
(cp/pedestal config-fn) ;; => cp/Pedestal record
(component/using (cp/pedestal config-fn) components-to-inject) ;; => cp/Pedestal record
```

There are two helper functions for managing components within Pedestal.
For injecting components from your system into the Pedestal request map:

* Build and define an interceptor chain which will inject those components: `(def component-interceptors (cp/component-interceptors components-to-inject))`

* Then, you can prepend said chain into your routes: ``["/api" :get (into component-interceptors [http/json-body `my-handler])]``

* Now, you can get your components from the request map directly: `(defn my-handler [{:keys [db sms] :as request}] ...)`

* Optionally, you can use namespace-qualified keys instead of plain `:db`, etc.

## Legacy API

* `(cp/using-component :component-key)` is returns an interceptor which adds a
* `(cp/using-component :component-key)` returns an interceptor which adds a
component to the Pedestal context, making it available via the request map.
* `(cp/use-component request :component-key)` returns the component from the
request map.

## Complete example

```clojure
(ns com.example.myapp.server
(:gen-class)
Expand All @@ -79,20 +95,21 @@ There are two helper functions for managing components within Pedestal.
(ring-resp/response "Welcome!"))

(defn login-page
[{:keys [form-params] :as request}]
(let [app (cp/use-component request :app)
{:keys [username password]} form-params]
[{:keys [form-params app db] :as request}]
;; now you can do something with `app` or `db`...
(let [{:keys [username password]} form-params]
(if-let [user (app/user-by-creds {:username username :password password})]
(ring-resp/response (str "Welcome, " (:user/first-name user) "!"))
(ring-resp/redirect (route/url-for :home-page)))))

(def routes #{["/" :get [(body-params)
`home-page]]
["/login" :post [(body-params)
(cp/using-component :app)
`login-page]]})
(def components-to-inject [:db])

(def component-interceptors (cp/component-interceptors components-to-inject))

(def routes #{["/" :get (into component-interceptors [(body-params) `home-page])]
["/login" :post (into into component-interceptors [(body-params) `login-page])]})

(defn pedestal-config-fn "Return Pedestal service map" []
(defn pedestal-config-fn "Return Pedestal service map" []
;; routes and other interesting bits
)

Expand All @@ -102,9 +119,9 @@ There are two helper functions for managing components within Pedestal.
(defn system [{:keys [pedestal db]}]
(component/system-map
:db (db/database (:conn-uri db))
:app (component/using (app/application) [:db]
:app (component/using (app/application) components-to-inject)
:pedestal (component/using (cp/pedestal (:config-fn pedestal))
[:app])))
(conj components-to-inject :app))))

(defn start-system [sys]
(component/start sys)
Expand All @@ -113,7 +130,7 @@ There are two helper functions for managing components within Pedestal.

(defn -main [& args]
(start-system (system config)))

```

## Testing
Expand Down
20 changes: 20 additions & 0 deletions src/com/grzm/component/pedestal.clj
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,23 @@
(-> context
(update-in [:request] #(dissoc % component-key))
(dissoc pedestal-component-key)))}))

(defn- context-injector [components]
{:enter (fn [{:keys [request] :as context}]
(reduce (fn [v component]
(assoc-in v [:request component] (use-component request component)))
context
components))
:name ::context-injector})

(defn component-interceptors
"For a given sequence of keywords representing the system components to be injected to the Pedestal context,
returns the necessary interceptors for you to prepend in your routes.

Each component will be associated under its given key, directly to the request map:

(:db request) -> <the :db component>"
[components-to-inject]
{:pre [(every? keyword? components-to-inject)]}
(conj (mapv using-component components-to-inject)
(context-injector components-to-inject)))