Fork me on GitHub
#meander
<
2021-09-21
>
wilkerlucio20:09:58

hello, trying to figure how to parte pedestal-like nested URL format using meander, for example, go from

["v1"
 ["/foo"
  ["/baz" {:post baz-handler}]]
 ["/bar"
  {:get  bar-handler
   :post post-bar}]]
to
[{:url "v1/foo/baz" :method :post :handler baz-handler}
 {:url "v1/bar" :method :get :handler bar-handler}
 {:url "v1/bar" :method :post :handler post-bar}]

wilkerlucio20:09:12

I guess I gotta use cata, but not sure how

ribelo20:09:58

(m/rewrite '["v1"
             ["/foo"
              ["/baz" {:post baz-handler}]]
             ["/bar" {:get bar-handler :post post-bar}]]
  (m/with [%1 (m/map-of !methods !handlers)
           %2 [!paths . (m/or %2 %1) ...]]
          %2)
  [{:url !paths :method !methods :handler !handlers} ...])

ribelo20:09:28

often with is simpler than cata

ribelo20:09:53

but now I see that it is not quite so, because the paths have been concatet

wilkerlucio20:09:56

cool, that almost solves, the missing part is accumulating the previous routes

ribelo20:09:09

honestly, I don't know how to do this

ribelo20:09:30

but it'll keep me busy tonight xD

wilkerlucio20:09:07

haha, would love to hear, I’m trying to figure on my end as well, if I figure I’ll post here

markaddleman20:09:59

Give this a try

markaddleman20:09:02

The first rewrite does the data re-organization that you're looking for but suffers from nested vectors (this is often a result of using cata).

markaddleman20:09:15

The second rewrite "flattens" the structure

wilkerlucio20:09:02

cool, I guess the last rewrite can be replaced with flatten, got the same result here

wilkerlucio20:09:42

not sure if its a fixable issue, but when I tried with a real longer structure, it did stack overflow 😅

2
markaddleman20:09:42

It's funny - I'm so used to using meander that I reached for meander to flatten the data without even thinking of flatten 🙂

markaddleman20:09:26

cata does stack-based recursion so a sufficiently deep structure could result in an overflow, I guess

markaddleman20:09:39

Let me see if I can rewrite it to avoid some of the recursion

wilkerlucio20:09:52

I don’t think its that deep, from a quick look I guess its less than 10 depth

markaddleman20:09:02

Can you post the structure?

ribelo20:09:12

stack overlow occurs when cata loops indefinitely

markaddleman20:09:14

I assume you're getting the SO from the first rewrite?

wilkerlucio20:09:22

sadly no, but there are a few other patterns that needs to be taken in account

wilkerlucio20:09:29

and I guess those are screwing it up

markaddleman20:09:56

Unfortunately, I'm not very familiar with these routing data structures

ribelo20:09:06

(m/rewrite 1
  (m/app inc ?x)
  (m/cata ?x))

ribelo20:09:46

I recommend using cata with instructions

wilkerlucio20:09:51

I can make a shorter version that has more of those features, let me check

ribelo20:09:05

is more readable and harder to error with an infinite loop

ribelo20:09:28

(m/rewrite [:start 1]
  [:start (m/app inc ?x)]
  (m/cata [:end ?x])
  [:end ?x]
  ~(str "end with ?x = " ?x))

ribelo20:09:27

you can easily set checkpoints and also check which instruction will be executed next

wilkerlucio20:09:02

found a simple stack overflow example:

wilkerlucio20:09:03

'["v1"
         ["/foo"
          ["/baz" {:post baz-handler}]
          ["/other" []]]
         ["/bar"
          {:get  bar-handler
           :post post-bar}]]

wilkerlucio20:09:14

sometimes instead of a map, its a vector with interceptors on the right side

markaddleman20:09:31

ah, yeah. I can see how this would overflow

markaddleman20:09:41

I'll take a crack at this data structure

wilkerlucio20:09:19

feels a little ambiguous case, curious to see what you come with for that 😉

markaddleman20:09:28

you read my mind

markaddleman20:09:46

unctions? fI'm guessing the vector of interceptors is always a vector of

markaddleman20:09:05

What does the result for this look like?

wilkerlucio20:09:04

the real one looks more like this:

wilkerlucio20:09:06

'["v1"
        ["/foo"
         ["/baz" {:post baz-handler}]
         ["/other" [[:interceptor
                     [{:features    {:policy-authz-enabled? false},
                       :interceptor [:scope
                                     {:scope "xxx"}]}
                      {:features    {:policy-authz-enabled? true},
                       :interceptor [:scope
                                     [{:policy "yyy"}]]}]]]]]
        ["/bar"
         {:get  bar-handler
          :post post-bar}]]

wilkerlucio20:09:29

but I’m not sure all possible variations (like if they always start with double vector)

markaddleman20:09:02

I've got a meeting in 5 minutes but I'll pick this up after

wilkerlucio20:09:24

thanks, I’ll keep playing here too

wilkerlucio20:09:39

seems like a notable difference is that those never start with strings at the beginning of the vector

wilkerlucio20:09:42

only routes do that

ribelo20:09:20

(m/rewrite '["v1"
             ["/foo"
              ["/baz" {:post baz-handler}]]
             ["/bar" {:get bar-handler :post post-bar}]]
  (m/with [%1 (m/map-of !methods !handlers)
           %2 [(m/app #(str (last !paths) %) !paths) . (m/or %2 %1) ...]]
    %2)
  [{:url !paths :method !methods :handler !handlers} ...])

ribelo20:09:36

I can't make it any shorter @U066U8JQJ

wilkerlucio21:09:44

on the large example it didn’t match though =/

wilkerlucio21:09:49

but I’ll play with it

wilkerlucio21:09:09

it probably because of the interceptors in the middle

ribelo21:09:17

what should be returned from the longer one?

ribelo21:09:55

\other should be omitted?

wilkerlucio21:09:24

omitting other is good

wilkerlucio21:09:04

the longer one currently returns nil, so something is going bad in the middle, I’m trying to get a case to reproduce with your solution

ribelo21:09:26

(m/rewrite '["v1"
             ["/foo"
              ["/baz" {:post baz-handler}]
              ["/other" [[:interceptor
                          [{:features    {:policy-authz-enabled? false} ,
                            :interceptor [:scope
                                          {:scope "xxx"}]}
                           {:features    {:policy-authz-enabled? true} ,
                            :interceptor [:scope
                                          [{:policy "yyy"}]]}]]]]]
             ["/bar"
              {:get bar-handler
               :post post-bar}]]
  (m/with [%1 (m/map-of !methods !handlers)
           %2 [(m/pred string?) _]
           %3 [(m/app #(str (last !paths) %) !paths) . (m/or %3 %2 %1) ...]]
    %3)
  [{:url !paths :method !methods :handler !handlers} ...])

ribelo21:09:02

\other catches under %2, which is defined in the simplest possible way, [string? whatever]

wilkerlucio21:09:45

here is an example that’s going off, but not the main problem yet:

wilkerlucio21:09:47

(m/rewrite '["v1"
               ["/foo"
                [:some-interceptors]
                ["/baz" {:post baz-handler}]
                ["/other" [[:interceptor
                            [{:features    {:policy-authz-enabled? false},
                              :interceptor [:scope
                                            {:scope "xxx"}]}
                             {:features    {:policy-authz-enabled? true},
                              :interceptor [:scope
                                            [{:policy "yyy"}]]}]]]]]
               ["/bar" {:get bar-handler :post post-bar}]]
    (m/with [%1 (m/map-of !methods !handlers)
             %2 [(m/pred string?) _]
             %3 [(m/app #(str (last !paths) %) !paths) . (m/or %3 %2 %1) ...]]
      %3)
    [{:url !paths :method !methods :handler !handlers} ...])

ribelo21:09:11

probably with a more complicated example it will fall in a different place

wilkerlucio21:09:22

(in this case the :some-interceptors should be ignored)

wilkerlucio21:09:38

anyway you guys are meander masters, its already really helpful 🙇

ribelo21:09:23

not so much I see that it is probably wrong, because it does not return a map with the path :url foo/bar 😕

ribelo21:09:39

but I'm glad I could help a little.

ribelo21:09:20

It would be nice if @U06MDAPTP offered a solution

ribelo21:09:02

I have no idea how to better concatenate urls into one, and what I've done doesn't always work

wilkerlucio21:09:05

the examples you posted are nice as well, I’ll spend some time to understand and learn those patterns 🙂

markaddleman21:09:37

Thanks for showing with @U0BBFDED7 ! I was aware of it but I don't think I've ever used it before

wilkerlucio02:09:18

this worked!

(m/rewrite
    (-> data :ig/system :pipo.module/web-routes :routes ffirst)
    
    (m/with [%route-map (m/map-of !methods !handlers)
             %item [(m/pred string?) _]
             %interceptors [(m/pred keyword?) . _ ...]
             %all [(m/app #(str (last !paths) %) !paths) . (m/or %all %item %interceptors %route-map) ...]]
      %all)
    [{:url !paths :method !methods :handler !handlers} ...])

wilkerlucio02:09:42

thank you again folks!

wilkerlucio02:09:11

I celebrated too soon, @U0BBFDED7 I found this way to concatenate is not doing the stack as expected, on the base example it outputs:

[{:url "v1", :method :post, :handler baz-handler}
 {:url "v1/foo", :method :get, :handler bar-handler}
 {:url "v1/foo/baz", :method :post, :handler post-bar}]

wilkerlucio02:09:34

but it should be

({:url "v1/foo/baz", :method :post, :handler baz-handler}
 {:url "v1/bar", :method :get, :handler bar-handler}
 {:url "v1/bar", :method :post, :handler post-bar})
(which is the result given frmo the @U2845S9KL solution)

wilkerlucio02:09:55

now trying to work more on the mark example to add the interceptors part

markaddleman02:09:39

I thought you guys already had a solution so I didn't pick up the interceptor piece. I'll give it a look tomorrow morning

wilkerlucio02:09:27

thanks, this is the example I’m working on top of now:

wilkerlucio02:09:29

'["v1"
        ["/foo"
         [:some-interceptors]
         ["/baz" {:post baz-handler}]]
        ["/bar" {:get bar-handler :post post-bar}]]

wilkerlucio02:09:47

you can forget the value as vectors, that wasn’t a thing, just the interceptors in the middle 🙂

wilkerlucio04:09:55

got it 😄 🎉

(-> '["v1"
        ["/foo"
         [:bla :me []]
         ["/baz" {:post baz-handler}]]
        ["/bar" {:get bar-handler :post post-bar}]]
      (m/rewrite
        [?path (m/map-of !action !handler)]
        [{:url ?path :method !action :handler !handler} ...]

        [?path-prefix [(m/pred keyword?) & _] & ?rest]
        (m/cata [?path-prefix & ?rest])

        [?path-prefix [?path-suffix & ?deeper-or-handlers]]
        (m/cata [(m/app str ?path-prefix ?path-suffix) & ?deeper-or-handlers])

        [?path-prefix . !deeper ...]
        [(m/cata [?path-prefix !deeper]) ...])
      flatten)
that was fun to learn! thanks folks

🎉 2
noprompt16:09:28

Sorry I missed this conversation. I was out with a few doctor appointments for myself and my son. 😅