Fork me on GitHub
#reitit
<
2018-11-27
>
valerauko06:11:11

hey i'm having this really weird problem

valerauko06:11:22

reitit loses the ring-session for some reason

valerauko06:11:30

given this handler

valerauko06:11:34

(defn debug-session
  [{:keys [session] :as req}]
  (let [new-uuid (java.util.UUID/randomUUID)
        session-id (get-in req [:cookies "ring-session"])]
    {:status 200 :body (str session "\n"
                            new-uuid "<br />\n"
                            session-id "<br />\n"
                            ring.middleware.session.memory/memory-store)
     :session (assoc session :foo new-uuid)}))

valerauko06:11:34

(def handler
  (ring/ring-handler
   (ring/router
    [["/hoge" {:get debug-session}]
     ["/fuga" {:get debug-session}]]
    {:data {:middleware [#(wrap-defaults debug-session site-defaults)]}})))
with these routes

valerauko06:11:57

reloading the same route has the session between requests alright

valerauko06:11:26

but as soon as i load the other route the session is ignored and a new session id is generated

valerauko06:11:46

stripping out routing

(def handler
  (wrap-defaults debug-session site-defaults))
it works

valerauko06:11:51

any idea what could cause this?

ikitommi07:11:45

@vale I think it should be

(def handler
  (ring/ring-handler
   (ring/router
    [["/hoge" {:get debug-session}]
     ["/fuga" {:get debug-session}]]
    {:data {:middleware [[wrap-defaults site-defaults]]}})))

valerauko08:11:49

behaves the same still

valerauko08:11:59

flushing .m2 and reinstalling all the dependencies didn't help either

valerauko08:11:35

behaves the same with using only wrap-session instead of wrap-defaults too

valerauko08:11:12

it works correctly with

(def handler
  (ring.middleware.session/wrap-session debug-session))

valerauko08:11:58

the memory store lives all right but it seems to be route-unique

valerauko08:11:07

is there anything in reitit that could cause that?

ikitommi08:11:41

@vale should not be, it's just a stateless router. Busy today, could investigate tomorrow if you write an issue with sample code.

ikitommi08:11:29

route-unique, oh, that! you should create one memory-store and give it as a reference to the mw.

ikitommi08:11:04

each route gets its own copy of all the mws.

ikitommi08:11:20

This should be highlighted in the docs. With Interceptors, it's ok as they can be chained as values, mws are HOFs and need to be created per endpoint.

valerauko08:11:04

yeah that fixed it

valerauko08:11:29

actually yesterday i had a very similar problem where we tried to with-redef a middleware wrapper and it didn't work as expected

valerauko08:11:35

i should've realized this was similar

valerauko08:11:40

thanks a lot!

lambdam09:11:42

Hello, I have a classic resource creation where routes distinguish only by HTTP verbs, not path :

["/events"
 ["" {:name :events-index
      :get c/events-index}]
 ["" {:name :events-create
      :post c/events-create}]]
I get a conflict error on creating the router : Router contains conflicting route paths Is it something feasible with reitit? Thanks

lambdam09:11:14

I found this in the documentation : https://metosin.github.io/reitit/ring/ring.html#request-method-based-routing On seeing the example

(def app
  (ring/ring-handler
    (ring/router
      [["/all" handler]
       ["/ping" {:name ::ping
                 :get handler
                 :post handler}]])))
it doesn't seem possible to separate two routes with two name and two verbs from a single path. I feel like it would be nice to be able to do this so that it would be possible to have RESTful routes à la Rails : https://guides.rubyonrails.org/routing.html#crud-verbs-and-actions. Or am I misunderstanding the way to use reitit? Thanks

ikitommi10:11:59

@dam you can disable the conflict resolution with a router option {:conflicts nil}, it fallbacks to linear router and the first matching servers the request.

lambdam10:11:25

@ikitommi thanks Does it mean that the general performance of the router goes from ~ O(1) to O(n)?

lambdam10:11:53

or only for conflicting routes?

ikitommi10:11:33

yes, and currently for all routes. there could be a router algo splits the routes into conflicting & non-conflicting, and uses linear-router for the latter and best possible for the first one.

ikitommi10:11:13

seen cases where there are 100+ routes and two legacy-endpoints conflicting. would be 98% fast still…

lambdam10:11:43

thanks for the detail

lambdam10:11:03

I'll keep my non-Rails-style routes for the moment so

ikitommi10:11:59

also, :name should be unique for the paths by design, so one needs to have a new identifier for the ring/http endpoints, could be something like :id?

lambdam10:11:49

I have this case with internationalized routes

ikitommi10:11:08

(def app
  (ring/ring-handler
    (ring/router
      [["/all" handler]
       ["/ping" {:name ::ping
                 :get {:id ::get, :handler get-thing}
                 :post {:id ::post, :handler add-thing}]])))

ikitommi10:11:05

if one want’s to identify the path + method pair…. (in reverse-routing). haven’t needed that myself

lambdam10:11:21

the main point I think is to be able to generate the path from ...

lambdam10:11:28

yes exactly

lambdam10:11:59

I heavily use the bi-directional routing and I feel it is a must have for routing.

lambdam10:11:10

What I really like with reitit is that I have a "one source of truth" for routes with a hiccup-like syntax (more readable than bidi (for me)).

👍 1
lambdam10:11:51

At the same place I can define route + name + handler

lambdam10:11:07

Thanks for the information and this great library that I was looking for for quite some time.

lambdam10:11:17

With exactly the following features: - Data driven route definition (à la Hiccup, even better) - Route + name + handler at the same place - Bi-directional routing (for path generation in templates) - AND last but not the least, middleware application on whole sub-path of routes (necessary for api + frontend)

lambdam11:11:35

Oh and to answer your question with the id, I would see something less nested like so:

(def app
  (ring/ring-handler
    (ring/router
      [["/all" handler]
       ["/en"
        ["/ping"
         ["" {:name ::ping-get
              :language :en
              :method :get
              :handler get-thing}]
         ["" {:name ::ping-add
              :language :en
              :method :post
              :handler add-thing}]]]
       ["/fr"
        ["/ping"
         ["" {:name ::ping-get
              :language :fr
              :method :get
              :handler get-thing}]
         ["" {:name ::ping-add
              :language :fr
              :method :post
              :handler add-thing}]]]])))
This comes from the use cases I have. The unique identifier of a route would be the set of values: #{path name language method} With path mandatory and :get as default for method.

ikitommi12:11:59

We have a similar use-case where we define a set of actions (commands and queries) as data and generate both routes for reitit and web-socket handler for eines. As reitit is just data, it's easy to generate the route tree from a domain data definitions. And it's easy to mix generated routes and traditional routes into a same app.

ikitommi12:11:45

reitit accepts also nested sequences, so it's easy to loop the domain definitions and emit a sequence of routes.

ikitommi12:11:55

@dam in your case, if you want to dispatch to different handlers based on language, you need some sort of double-dispatch: could be in front of reitit (generate multiple routers, one per each language) or inside: a generic ring-handler doing the lang-dispatch mounted to the paths.

ikitommi12:11:56

There are no generic guards in reitit, by design, yet.

mkvlr14:11:37

@dam we do this by using named routes and reitit matches internally, so they contain all the information