summaryrefslogtreecommitdiff
path: root/compojure-3.2/src/compojure/control.clj
blob: ea45c691eb6c834e1d9597d1828fe7b6215767c1 (plain)
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
;; Copyright (c) James Reeves. All rights reserved.
;; The use and distribution terms for this software are covered by the Eclipse
;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which
;; can be found in the file epl-v10.html at the root of this distribution. By
;; using this software in any fashion, you are agreeing to be bound by the
;; terms of this license. You must not remove this notice, or any other, from
;; this software.

(ns compojure.control
  "Various macros for controling program flow."
  (:use clojure.contrib.seq-utils))

(defmacro return
  "A do block that will always return the argument 'x'."
  [x & body]
  `(let [x# ~x]
     (do ~@body x#)))

(defmacro maybe
  "Returns (f x & xs) if x is not nil, otherwise returns nil."
  [f x & xs]
  `(if (not (nil? ~x))
     (~f ~x ~@xs)))

(defmacro domap
  "Similar to doseq, but collects the results into a sequence."
  [[item list] & body]
  `(map (fn [~item] ~@body) (doall ~list)))

(defmacro redef
  "Redefine an existing value, keeping the metadata intact."
  [name value]
  `(let [m# (meta #'~name)
         v# (def ~name ~value)]
     (alter-meta! v# merge m#)
     v#))

(defmacro decorate
  "Wrap a function in one or more decorators."
  [func & decorators]
  `(redef ~func (-> ~func ~@decorators)))

(defmacro decorate-with
  "Wrap multiple functions in a decorator."
  [decorator & funcs]
  `(do ~@(for [f funcs]
          `(redef ~f (~decorator ~f)))))

(defmacro decorate-bind
  "Wrap named functions in a decorator for a bounded scope."
  [decorator funcs & body]
  `(binding
     [~@(mapcat (fn [f] [f (list decorator f)]) funcs)]
     ~@body))

(defn apply-doc
  "Return a symbol and body with an optional docstring applied."
  [name doc? body]
  (if (string? doc?)
    (list* (with-meta name (assoc (meta name) :doc doc?)) body)
    (list* name doc? body)))

(defmacro deftmpl
  "Define a template function. Arguments are passed via key-value pairs.
  e.g. (deftmpl foo [bar baz] (+ bar baz))
       (foo :bar 1 :baz 2)"
  [name doc? & body]
  (let [[name params & body] (apply-doc name doc? body)]
   `(defn ~name
      ~@doc?
       [& param-map#]
       (let [{:keys ~params} (apply hash-map param-map#)]
        ~@body))))