Handler Metadata

166 views
Skip to first unread message

Brian Rowe

unread,
May 20, 2015, 9:49:11 AM5/20/15
to pedesta...@googlegroups.com
Hey,

We've written an interceptor that looks at the interceptor queue for interceptors that have metadata indicating they should have cljs brepl code injected into the returned HTML. Like so:

(defaround browser-repl
  ([context]
   (if-let [brepl (->> context
                       :io.pedestal.impl.interceptor/queue
                       (map meta)
                       (some :browser-repl))]
     (assoc context ::browser-repl (add-repl-fn brepl))
     context))
  
  ([context]
   (if-let [add-brepl (::browser-repl context)]
     (update-in context [:response] add-brepl)
     context)))

I've defined a route where the handler is a list. For this handler, the :browser-repl metadata is dropped. Here's the route definition:

[[["/" {:get [:root 
                 (with-meta (constantly (resp/resource-response "public/html/index.html"))
                   {:browser-repl {:js-file "/js/main.js"
                                   :goog-base-file "/js/dev/goog/base.js"
                                   :init-namespace "lease_pulse.main"}})]}]]]

Would pedestal benefit from a patch that promotes the metadata to the interceptor created here? Or is there a reason why it's not added to the interceptor that I'm unaware of?

Thanks!

Paul deGrandis

unread,
May 21, 2015, 7:55:14 AM5/21/15
to pedesta...@googlegroups.com
Hi Brian,

Thanks for reaching out!  Also, from the looks of it, you're using Pedestal like a total pro!
Usually if metadata on an interceptor (or handler, which is also just an interceptor) is needed, it's advised to just create that interceptor with the metadata you want.  In the example you attached, I would just create a top-level def for that endpoint:

(def root-handler (with-meta (interceptor {:enter (fn [ctx] ...)})

  {:browser-repl {:js-file "/js/main.js"
                         :goog-base-file "/js/dev/goog/base.js"
                         :init-namespace "lease_pulse.main"}}))

This should cause the interceptor API to just forward your interceptor unmodified with the metadata attached: https://github.com/pedestal/pedestal/blob/311646912a013cff1762efa9dd0deec0b5085777/service/src/io/pedestal/interceptor.clj#L53

You can use interceptors as route endpoints in the route table (in case you didn't know that).

Sometimes interceptors are created on the fly (the chain gets dynamically created), so we try to keep interceptor creation as fast and direct/simple as possible.  That said, if there are others who would benefit from metadata being forwarded in interceptor conversion, I'd like to hear about it.

Let me know if this works out for you!  If not, we'll come up with something else.

Cheers,
Paul

Brian Rowe

unread,
May 21, 2015, 9:34:15 AM5/21/15
to pedesta...@googlegroups.com
Well, thanks for the compliment, but credit belongs to Stu Hinson. He's the implementor of the interceptor!

Brian Rowe

unread,
May 21, 2015, 11:16:04 AM5/21/15
to pedesta...@googlegroups.com
If I'm reading the IntoInterceptor protocol correctly, it seems that if we def an interceptor and apply metadata to in in the route like so:

[[["/" {:get [:root (with-meta root-handler
                               {:browser-repl {:js-file "/js/main.js"
                                               :goog-base-file "/js/dev/goog/base.js"
                                               :init-namespace "lease_pulse.main"}})]}]]]

Then the metadata doesn't get dropped. I'm not sure having different behavior between this and the original route definition is desirable.

Either way, we'd like to be able to apply different brepl configurations based on where in the route structure they are, not based on the ultimate handler involved.

--
You received this message because you are subscribed to a topic in the Google Groups "pedestal-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/pedestal-users/Peoc1LheR8I/unsubscribe.
To unsubscribe from this group and all its topics, send an email to pedestal-user...@googlegroups.com.
Visit this group at http://groups.google.com/group/pedestal-users.

Brian Rowe

unread,
May 21, 2015, 2:10:16 PM5/21/15
to pedesta...@googlegroups.com
I suppose this route spec would work for us too:

[[["/" ^:interceptors [(->brepl-interceptor 
                          "/js/main.js"
                          "/js/dev/goog/base.js"
                          "lease_pulse.main")]
       {:get root-handler}]
  ["/foo" {:get some-handler}]]]

Though I'd rather not to have to make a sibling element of the root (or other cases where i don't want the interceptor to apply to all the "child" routes).

Is there another way to achieve this?

Brian Rowe

unread,
May 21, 2015, 2:12:28 PM5/21/15
to pedesta...@googlegroups.com
I should note that here ->brepl-interceptor would just return a :leave interceptor.

Alex Redington

unread,
May 26, 2015, 9:51:49 AM5/26/15
to Brian Rowe, pedesta...@googlegroups.com
Brian,

I just wanted to quickly note that I'm personally excited to see what you're doing with the Pedestal routing syntax.

That out of the way, is it possible to pull apart where and when you specify information from when it's assembled to be sent to a client? e.g. could root-handler specify with metadata on it and it alone that it's working with the lease_pulse.main namespace as part of def metadata, and another interceptor higher in the routing hierarchy consume that available metadata and synthesize it with the environment specific information like "/js/main.js" and "/js/dev/goog/base.js" to have all of the correct information available to set up brepl correctly?

My personal opinion is that adding more responsibility to ring handler -> pedestal interceptor coercion such that metadata on the ring handler is conveyed to the coerced interceptor might be adding more weight than is reasonable to that coercion.

--
You received this message because you are subscribed to the Google Groups "pedestal-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pedestal-user...@googlegroups.com.

Stuart Hinson

unread,
May 26, 2015, 12:21:08 PM5/26/15
to pedesta...@googlegroups.com, br...@rentpath.com
Very cool idea Alex. Brian and I are figuring out how to incorporate it into our prod usage now :)

I posted a short write up of the approach Brian and I used for an audience that's a bit less savvy with Pedestal


Appreciate any feedback! Prob send it direct so we don't spam the list

Paul deGrandis

unread,
May 27, 2015, 7:17:05 AM5/27/15
to pedesta...@googlegroups.com, br...@rentpath.com
Another fantastic post Stuart, thank you.  Alex's advice is also sound and worth considering.  Whenever possible, we typically utilize the fact that routes can be constructed/inspected/adjusted at start-up to produce "behaviors" rather than constantly inspecting per request.  All in all, interesting stuff!

I would personally use the interceptor API directly instead of the `around` helper, which might read a little clearer.

(defn browser-repl
  "produces an interceptor that scans the context's queue on enter for
  brower-repl metadata, calls inject-repl on leave if found"
  [inject-repl]
  (interceptor
   {:name ::browser-repl
    :enter (fn [context] ;; fn to call on :enter
             (cond-> context
               (some->> context ;; scan the queue for :browser-repl metadata

                        :io.pedestal.impl.interceptor/queue
                        (map meta)
                        (some :browser-repl))

       (assoc :include-browser-repl true))) ;; add a flag to inject the repl
    :leave (fn [{response :response :as context}] ;; on :leave
             (cond-> context
             (:include-browser-repl context) ;; did we find browser-repl metadata?
             (update :response inject-repl)))})) ;; add a repl!

- Paul


Brian Rowe

unread,
May 27, 2015, 9:46:39 AM5/27/15
to Paul deGrandis, pedesta...@googlegroups.com
Alex, Paul,

Great feedback!

Paul, it had occurred to me that, as an optimization, we could pull the (some->> context ... (some :browser-repl)) out into a predicate that operates on just the interceptor queue and memoize it. It sounds like that may not be the best strategy in sophisticated Pedestal applications. I like your suggestion to do it once at system start time much better!

Very cool!
Reply all
Reply to author
Forward
0 new messages