Fork me on GitHub
#reitit
<
2018-11-13
>
ikitommi06:11:28

One option is to push the compojure-routes under the default-handler in reitit-ring. One routes function less on the way to reitit routing tree. Don't think it shows on criterium...

ikitommi06:11:41

How did the switching go? Any docs missing to support that?

levitanong12:11:12

Hi all, is there an idiomatic way to deal with several paths leading to one match? Use case is supporting legacy paths.

ikitommi13:11:36

could you use a shared value?

(let [endpoint {:get (constantly {:status 200, :body "legacy"})}]
  [["/a" endpoint]
   ["/b" endpoint]])

levitanong13:11:12

@ikitommi unfortunately no, because i am doing client side routing as well. I’m using fulcro, and it has some routing machinery which depends primarily on the [:data :name] of the reitit match

ikitommi13:11:25

so you would like to have multiple routes return same :name?

levitanong13:11:51

@ikitommi yeah, i’m aware though, that reitit is opinionated against this 😅

ikitommi14:11:05

could you add a new key like :namez and use that instead? Or add :alias and use it if exists, :name otherwise?

ikitommi14:11:00

reverse routing is kinda hard if you have multiple :names....

ikitommi14:11:22

btw, fulcro+reitit sounds awesome!

levitanong14:11:26

@ikitommi i never thought of that! Will experiment. Thanks for the ideas 😄

levitanong14:11:47

fulcro+reitit is a better match than bidi, tbh.

phil14:11:51

Might not be a reitit question, really. When I call (app ...) in my tests, the body comes back as an InputStream which I have to decode/format :body myself.

phil14:11:18

Should I not get :body back already decoded for me thanks to muuntaja/format-response-middleware in my middleware list?

ikitommi14:11:36

@phil the format-response-middleware encodes the body to whatever the user requested. So, in tests, you have to decode it with something like (m/decode m/instance "application/edn" data) (we use mostly edn in tests).

ikitommi14:11:32

You could also disable the response formatting mw for tests, but you would not be testing the whole app. Could leave room for serialization bugs.

phil14:11:54

Setting the Accept header doesn't do the same thing?

ikitommi14:11:45

Setting the Accept header tells the formatter to encode the data into something, it's not about decoding.

ikitommi14:11:41

There could be a testing helper that reads the request headers and auto-decodes the response body accordingly.

phil14:11:10

Ok, good to know. Cheers, again, @ikitommi.

ikitommi14:11:22

But it's just few lines of code.

👍 4
levitanong14:11:44

@ikitommi I’m looking at reitit.frontend, and I think the history records should be matching by rf/match-by-path instead of the vanilla reitit/match-by-path. This way, it automatically takes advantage of query-params support. https://github.com/metosin/reitit/blob/master/modules/reitit-frontend/src/reitit/frontend/history.cljs#L78

ikitommi14:11:52

ping @juhoteperi, the frontend-reitit lead.

ikitommi14:11:36

@phil dirty, but should work:

ikitommi14:11:40

(ns user
  (:require [reitit.ring :as ring]
            [reitit.ring.middleware.muuntaja :as muuntaja]
            [muuntaja.core :as m]))

(def app
  (ring/ring-handler
    (ring/router
      ["/" (constantly {:status 200, :body {:kikka "kukka"}})]
      {:data {:muuntaja m/instance
              :middleware [muuntaja/format-middleware]}})))

(->> (app {:uri "/", :request-method :get})
     :body
     (m/decode m/instance "application/json"))
; {:kikka "kukka"}

(defn parse-body [response]
  (if-let [ct (some->> (get-in response [:headers "Content-Type"]) (re-find #"^(.*?)(?:;|$)") second)]
    (->> response :body (m/decode m/instance ct))))

(-> {:uri "/", :request-method :get} app parse-body)
; {:kikka "kukka"}

ikitommi14:11:57

and with edn:

(-> {:uri "/"
     :request-method :get
     :headers {"accept" "application/edn"}} 
    app 
    parse-body)
; {:kikka "kukka"}

phil14:11:53

Awesome, thanks.

juhoteperi15:11:12

@levitanong Does this cause any problems? The html5 history impl currently ignores query params when checking if anchor link matches routes, and then appends the original query params to pushState call

juhoteperi15:11:07

Parsing query strings from the anchor href and then reconstructing query string would be unnecessary work

levitanong16:11:38

@juhoteperi but the query map contains important information for state transformations. I'm currently trying to port over functionality from pushy, but I'm not sure how to move forward :o

juhoteperi16:11:44

@levitanong The result from that match call is not used anywhere. If the anchor href matches route (this match call) then pushState is called with the href and that will trigger on-navigate event which will properly parse the path, including query string.

juhoteperi16:11:06

and on-navigate will use reitit.frontend match-by-path to get the proper match

levitanong16:11:58

@juhoteperi Hmm. I'm not getting the query map with the match on start though.

kanwei16:11:20

@ikitommi migrating from compojure to reitit (still in progress) was straightforward but still not painless

kanwei16:11:57

1. i wasn't sure why ring-handler took a default-handler when we can just use "routes" to compose things

kanwei16:11:43

2. trying to add muuntaja was very confusing since in some docs, you used {:data {:muutaja m/instance ..}} whereas trying to add it to the ring-handler, there's just :middleware

kanwei16:11:42

3. reitit doesn't coerce certain responses like compojure does, I later found out this was a design decision after looking through github issues, and this difference isn't documented (i assume everyone comes from compojure)

juhoteperi16:11:55

@levitanong Did you try with examples/frontend from the repository? You need to change the :use-fragment to false but after that it should work with HTML5 history. "Item 2" link contains query string.

levitanong17:11:30

OH i didn’t realize :use-fragment defaulted to true. Thanks for the pointer!

levitanong18:11:17

yup that fixed it. Thanks man!

kanwei16:11:43

4. muuntaja in 0.6 no longer encodes if Content-Type is set, so you're screwed (with little feedback as to why) if you get the middleware order incorrect, especially when using things like ring-defaults which adds content-type by default

juhoteperi17:11:26

@kanwei 1. I think one reason is that composing multiple "top level" handler functions can be confusing if you are using certain middlewares with side-effects, this is common with middleware that read the request body input stream, because the stream can be read only once. With default-handler everything is "inside" single handler function so it can be more obvious which middlewares affect handler.

kanwei17:11:55

i actually ran into that exact problem with the stream being closed

juhoteperi17:11:57

Or maybe Reitit runs the middleware only if request matches routes, in which case middlewares wouldn't cause side-effects so this wouldn't be a problem. I'm not sure.

kanwei17:11:05

ended up moving the middleware to outside all the handlers and "route" compositions

kanwei17:11:51

the arity of ring-handler is also confusing cause you have to do (ring-handler handler nil {options})

kanwei17:11:06

if you accidentally do (ring-handler handler {options}) you don't get a warning either

juhoteperi17:11:49

Maybe ring-handler should assert default-handler is fn? or nil. Map could be used as function but it wouldn't probably make any sense in this case.

ikitommi18:11:07

@kanwei thanks for the input. Juho already wrote an issue about the arity. Goal is to be able to put reitit to the topmost handler and everything under it, need to clear that out in the docs. This way, things will happen at a right time (conditional mounting of mw, only after match, file io as a last effort etc). In libs like ring-defaults, a lot of good things happen, but mostly at a bad time.

ikitommi18:11:05

all help on the documentation most welcome.

kanwei18:11:27

is there like a library that generates ring responses? like text/html, etc

kanwei18:11:01

i guess just using ring.util.response content-type isn't the worst thing ever

kanwei21:11:59

thanks, that helps

kanwei21:11:15

also I just ripped out all the requires from ring-defaults and am rolling out all the middleware manually now 🙂

kanwei21:11:37

demagifying our app is probably good in the long term