I have a question about syntax for route names. I find this does register a route name: ["/threads/create" threads.create] This doesn't: ["/threads/create" threads.create {:get (fn [req] nil)}] But this does: ["/threads/create" {:name threads.create :get (fn [req] nil)}] From the docs, it implies on my reading that the second example above should work. When I define these routes and call reitit-ring/route-names on a reitit-ring/router created with them, they don't appear.
Also a little discombobulated by the docs. Are the examples in the docs on route syntax real examples? It seems to me they aren't, because paths on their own don't really serve a purpose, a router routing to what? Maybe it would be better to have a full realistic example of a routing table, instead of covering successive edge cases which seem more like developer notes? I mean, a definitive list of what is and isn't legal is one way of documenting things, just saying how to achieve a real example might be as or more effective?
(I know there are also source examples in the repo).
which document are we talking about? this one? https://github.com/metosin/reitit/blob/master/doc/basics/route_syntax.md
yes
I'm not defending that document, but I'm not seeing the ["path" ::name {:foo :bar}] syntax mentioned
Does the service function always return the http response? (I mean the ring response map)
oh yeah that's usually outside the service function
i was veering toward the idea that the service function would (nearly) always return the ring response map itself; be the last thing called by the inline (anonymous) handler. unless there was some reason the anonymous handler needed to do it; after the service function returned. but i don't have examples yet.
So you are saying the service function usually doesn't return the http response?
Actually I think I've seen both. So the service function can return the ring response like {:status 200 :body {:name "bob" :id 123}}, or then just the domain object, {:name "bob" :id 123} and the reitit handler function then wraps this. So something like
:handler (fn [req]
(let [id (... get param from req ...)
user (service/get-user id)]
(if user
(ok user)
(not-found))))
this latter form is nice when you want to use service/get-user in other, non-ring contexts
but there are no right answers here, it's up to you which pattern you want to use
I'll see if I can improve the doc later today...
So these are just 'equivalent' (if this is all you want - with no route destination; not realistic example)
[["/ping" ::ping]
["/pong" {:name ::pong}]]
but if you're actually using a :get as a destination for the route, and thereby need an options map, then you can't use ping?
i didn't see that at all (and is that what should happen?)
when i attempted to look at the source, it seemed that isn't the way it's intended? (expansions i think its called).Yeah, those are equivalent. I think the docs should primarily talk about the map format and mention the ::ping as a shortcut later.
I'm not quite following the rest of your message, too many negations for me
if I use an options map for other reasons, then i can't use the first form, of keyword directly in the vector? (to be expanded into the map as :name) ?
No
yeah, you're right, you can only have a bare keyword or a map as the route argument, not both
trying to work out why i'm eccentric for wanting to do that!
heh
TBH I haven't seen anyone use name-based routing in production, so that's why I can't really tell you if you're alone in this
oh, that info helps!
i can proceed now though, thanks 🙂
no problem, and sorry for the docs 😁
i also keep a log of my beginner experiences; qu's encountered and solutions; to convert into a little getting started guide from my own angle in due course.
IMO no one set of docs can cater to all needs (e.g. exhaustive reference vs beginner guide), just like no one piece of software can or should try to cover all possible needs equally. So it's about the decision on the target audience really 🙂
but also fixing the obvious omissions and mistakes, like in this case
I've noticed that the examples for reitit-ring i've seen all use anonymous function defs for the :handler within the options map (rather than referring to a function defined elsewhere). Would it be correct to observe that this makes those functions untestable? (I guess that means unit tests). (Background, also, related - I started out by using the thread first macro in which to state the route vector, followed by ring middleware in the traditional ring style. One drawback there is that if there are any typos/compile errors, since everything is within a macro, the error messages are somewhat... opaque). So some advantages potentially in named function defs. This said, I certainly appreciate the readability of anonymous functions inline next to the paths, - it does makes the whole app nicely readable top to bottom.
There are a couple of different ways to do it. Some people like always using a named handler function. I prefer a style with an anonymous function that does parameter-munging that then calls a service function with a nice API. That is, I'm trying to isolate the HTTP-specific stuff in the reitit definition.
It's usually best to test the handler functions as part of the router. You can e.g. just call the ring app with mocked request maps and check outputs. That way you get to test the integration between your middleware, coercions and your handler function.
The examples probably use anonymous functions just for compactness/readability.