File: patterns.md

package info (click to toggle)
bidi-clojure 2.1.6-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 252 kB
  • sloc: makefile: 20; sh: 13
file content (80 lines) | stat: -rw-r--r-- 2,889 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

DEPRECATED - while there are some useful tidbits here, it is recommended that you solve circular dependencies with co-dependencies, as shown by bootsrap-cover :-

    $ lein new modular foo bootstrap-cover

-------------

# bidi Patterns

Here are some patterns that you may find useful in your development.

## Handler map promise

### Situation

You have a bunch of REST resources, each of which might need to form
hyperlinks to one or more of the others. There may even be a mutual
dependency. Which handler do you create first?

### Solution

Use a promise to access a handler map, containing entries for each
handler keyed with keywords. The promise must be delivered prior to
calling dereferencing it in a call to `path-for`. This is ensured by the
`make-handlers` function, which delivers the promise prior to returning
the result, so the promise cannot escape the handler construction phase.

### Discussion

Here is an example which demonstrates the technique. It uses a pair of
[Liberator](http://clojure-liberator.github.io/liberator/) resources to
create a REST API, which need to create
[hyperlinks](http://en.wikipedia.org/wiki/HATEOAS) to each other.

In the code below we assume that the bidi routes are available in the
request, under the `:request` key. Every application is different, and its
up to the bidi user to ensure that request handlers have access to the
overall route structure.

Notice how both resources use the `path-for` function to form paths to
the other resource.

The `make-handlers` function creates a promise to a map containing each
handler, referenced by a known keyword, which can be used to look up the
handler, and thereby form the path to it.

```clojure
(defresource contacts [database handlers]
  :allowed-methods #{:post}
  :post! (fn [{{body :body} :request}]
           {:id (create-contact! database body)})
  :handle-created (fn [{{routes :routes} :request id :id}]
                    (assert (realized? handlers))
                    (ring-response
                     {:headers {"Location" (path-for routes (:contact @handlers) :id id)}})))

(defresource contact [handlers]
  :allowed-methods #{:delete :put}
  :available-media-types #{"application/json"}
  :handle-ok (fn [{{{id :id} :route-params routes :routes} :request}]
               (assert (realized? handlers))
               (html [:div [:h2 "Contact: " id]
                      [:a {:href (path-for routes (:contacts @handlers))} "Index"]])))

(defn make-handlers [database]
  (let [p (promise)]
    ;; Deliver the promise so it doesn't escape this function.
    @(deliver p {:contacts (contacts database p)
                 :contact (contact p)})))

(defn make-routes [handlers]
  ["/" [["contacts" (:contacts handlers)]
       [["contact/" :id] (:contact handlers)]
       ]])

;; Create the route structure like this :-

(-> database make-handlers make-routes)

```