ring

mathpunk 2019-01-10T19:19:43.011300Z

I have this data tool and I'm trying to create an interface for it, so I'm trying to write a server

mathpunk 2019-01-10T19:20:16.011900Z

I'd like it to print some text output if you curl, and some markup if you access it another way

mathpunk 2019-01-10T19:20:22.012100Z

(defroutes app-routes
  (GET "/" [] (html (view/last-night [])))
  (GET "/agent" {:keys [headers params] :as request}
       (if-let [agent (get headers "user-agent")]
         (str "Your user agent is " agent)
         (html [:div.agent
                [:h2 "Welcome,"]
                [:p "Mysterious friend"]])))
  (GET "/pipeline/:id" [id] (html (view/pipeline id)))
  (route/not-found "Not Found"))

mathpunk 2019-01-10T19:20:55.012800Z

In these routes, I've figured out how to get at the headers in the /agent route. And I've figured out how to get data out of the URL in the /pipeline/:id route

mathpunk 2019-01-10T19:21:24.013500Z

Can these be combined? E.g., can I have a route /pipeline/:id that accesses the headers?

mathpunk 2019-01-10T19:22:22.014700Z

I've looked at the Compojure Destructuring document but, I'm not clear on whether that :id is a 'param' or not

seancorfield 2019-01-10T19:54:32.015500Z

If you use functions as handlers, instead of just code blocks, they will be passed the whole Ring request hash map.

seancorfield 2019-01-10T19:55:50.016700Z

The declaration of variables in routes -- [id] in your code -- is good for coercion of parameter types and documentation (and very, very simple handlers) but not much use beyond that really.

seancorfield 2019-01-10T19:56:25.017300Z

So your handlers could be (fn [{:keys [headers params] :as request}] …) or named functions.

seancorfield 2019-01-10T19:56:37.017600Z

Does that help @mathpunk?

mathpunk 2019-01-10T19:57:10.018200Z

kiiiiind of.... does that mean that the segments of the URL are in the request itself?

mathpunk 2019-01-10T19:57:39.019100Z

like I'm just looking at this form, (GET "/pipeline/:id" [id] (html (view/pipeline id))) and it looks like magic that turns "a bit of URL in the browser bar" into "a bound var"

mathpunk 2019-01-10T19:58:30.020500Z

so, if use that destructuring form, I'm responsible for splitting up (:compojure-uri request) myself

seancorfield 2019-01-10T19:59:09.021300Z

(GET "/pipeline/:id" [] show-pipeline) where

(defn show-pipeline [{:keys [headers params] :as request}] (html (view/pipeline (:id params))))

mathpunk 2019-01-10T20:00:42.021900Z

ok I think I see it

seancorfield 2019-01-10T20:01:14.022600Z

I try to avoid Compojure's destructuring to be honest -- it isn't powerful enough in the general case.

mathpunk 2019-01-10T20:01:41.023200Z

I didn't realize that the params and the URI had anything to do with each other.... my mental model was, "headers are key-value pairs you're not shown, and params are things after a question mark"

mathpunk 2019-01-10T20:02:33.023900Z

hmm... I have tried using Ring directly but I very quickly got overwhelmed by options I didn't understand

mathpunk 2019-01-10T20:03:06.024600Z

(there's a lotta stuff in a request and a response)

seancorfield 2019-01-10T20:13:41.025900Z

Looking at the source code, I'm not quite sure how it ends up working with function values in the handler slot (but it definitely does) so I need to play with macroexpand in the REPL a bit...

seancorfield 2019-01-10T20:22:34.028500Z

...ah, OK, I get it now. So the bound symbols are available literally in the handler form, whatever that is, so this would work:

(GET "/pipeline/:id" [id] (fn [req] (html (view/pipeline id))))
and you could destructure req to get at the parameters etc. Looks like it puts id directly in the request hash map as well, but I'd have to play with that some more.

seancorfield 2019-01-10T20:23:34.029200Z

(I've never bother to dig through the source to understand it before -- "It. Just. Works." so I haven't needed to)

seancorfield 2019-01-10T20:48:28.030800Z

@mathpunk Just as a follow-up

user=> (GET "/pipeline/:id" [id] (fn [req] (println 'id id) (println 'req req) "Hi!"))
#object[compojure.core$wrap_route_matches$fn__2291 0x76134b9b "compojure.core$wrap_route_matches$fn__2291@76134b9b"]
user=> (*1 {:uri "/pipeline/42" :request-method :get})
id 42
req {:uri /pipeline/42, :request-method :get, :route-params {:id 42}, :params {:id 42}, :compojure/route [:get /pipeline/:id]}
{:status 200, :headers {"Content-Type" "text/html; charset=utf-8"}, :body "Hi!"}
So we can see that the parsed value ends up in :route-params (and, this, in :params as well) and is also bound as a local symbol for the form supplied in the handler slot.

mathpunk 2019-01-10T20:48:40.031Z

O_O

mathpunk 2019-01-10T20:49:49.031900Z

I have grabbed all of this text, put it somewhere safe, and I will stare at + experiment with it 🙂

seancorfield 2019-01-10T20:51:12.033300Z

If the binding form is a vector, it just binds the route parameter values it seems. If the binding form is a hash map, then it binds the whole request map.

seancorfield 2019-01-10T20:51:30.033600Z

That was one of the pieces I was missing.

mathpunk 2019-01-10T20:51:49.033900Z

thanks Sean! helpful as always 🙏:skin-tone-2:

seancorfield 2019-01-10T20:52:54.034800Z

Helping other people often teaches me new stuff and improves my understanding of tech. I'm not as altruistic as people might think 🙂

mathpunk 2019-01-10T20:53:12.035Z

hahaha