0

I'm trying to write a macro to conditionally wrap compojure handler in a middleware only in certain environments. I seem to be lost in the evaluation order and what/how should I quote. My current attempt is as follows:

(defn empty-middleware [handler]
  (fn [request] (handler request)))

(defmacro prod [handler middleware]
  (if (= "production" (System/getenv "APP_ENV"))
    (middleware handler)
    (empty-middleware handler)))

Desired usage is:

(in/prod (fn [handler]  (airbrake/wrap-airbrake handler config/airbrake-api-key)))

--- EDIT ---

Some more information: in/prod is supposed to be used inside threading macro that wraps routes in a number of middlewares like:

(-> handler
    middleware1
    middleware2
    (in/prod (middleware3 middleware-3-param1))

both middleware3 and in/prod need handler as parameter. Wrapping middleware3 in parenthesis evaluates without possibility to pass handler as a parameter. Hence I thought a macro is needed. I worked out how to make in/prod a function passing middleware3 and middleware params as parameters:

(defn prod [handler middleware & middleware-params]
  (if (= "production" (System/getenv "APP_ENV"))
    (apply middleware handler middleware-params)
    handler))

It changes the syntax a bit though. The call looks like:

(-> handler
    middleware1
    middleware2
    (in/prod middleware3 middleware-3-param1)

How would I go around to being able to use syntax like:

(in/prod (middleware3 middleware-3-param1)
sumek
  • 26,495
  • 13
  • 56
  • 75
  • 1
    I don't see any reason to use a macro here. Can you provide more details as to why this must be a macro and not an `fn` that returns a handler? – Kyle Feb 02 '15 at 19:35
  • well, maybe not. initially my thinking was that I need macro not to evaluate middleware. it was before I've wrapped it in the function. ideally I would like to be able not to wrap "airbrake/wrap-airbrake" inside a function, aka have less boilerplate. – sumek Feb 02 '15 at 21:18
  • Wouldn't the desired usage be more like: `(in/prod (airbrake/wrap-airbrake handler config/airbrake-api-key))`? With your usage you could simply use a function. And `prod` expects two parameters and you are only passing one, is that a typo? – Rodrigo Taboada Feb 02 '15 at 21:55

1 Answers1

1

You could need this to be a macro iff the middleware you wish to apply is also a macro and thus you are suffering from macro-contagion. In that you would simply want a macro that returns one of two possible s-expressins to be included in the resulting code. One where handler is called directly and one where it is wrapped in the given middleware.

(defmacro prod [handler middleware]
  (if (= "production" (System/getenv "APP_ENV"))
    `(~middleware ~handler) ;; or (list middleware handler)
     handler))

If you are not currently suffering from macro-contagion while unable to fix that by removing the macro elsewhere, then you do not need to use such a macro and the code in your example can simply be used as a function rather than as a macro. The empty-middlware bit is not required in either case.

Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
  • Indeed the implementation of `empty-middleware` is simply a more restrictive version of `identity`. – amalloy Feb 03 '15 at 21:30