Fork me on GitHub
#ring
<
2019-01-10
>
mathpunk19:01:43

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

mathpunk19:01:16

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

mathpunk19:01:22

(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"))

mathpunk19:01:55

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

mathpunk19:01:24

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

mathpunk19:01:22

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

seancorfield19:01:32

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

seancorfield19:01:50

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.

seancorfield19:01:25

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

mathpunk19:01:10

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

mathpunk19:01:39

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"

mathpunk19:01:30

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

seancorfield19:01:09

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

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

mathpunk20:01:42

ok I think I see it

seancorfield20:01:14

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

mathpunk20:01:41

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"

mathpunk20:01:33

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

mathpunk20:01:06

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

seancorfield20:01:41

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...

seancorfield20:01:34

...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.

seancorfield20:01:34

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

seancorfield20:01:28

@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.

mathpunk20:01:49

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

seancorfield20:01:12

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.

seancorfield20:01:30

That was one of the pieces I was missing.

mathpunk20:01:49

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

seancorfield20:01:54

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