Om.next/tempid support.
Om.next defines these custom types called tempids. https://github.com/omcljs/om/blob/ee4c7ac33934fcd9b3ea663045ced1408dd7df24/src/main/om/tempid.cljc#L8
I want bidi to support routing to them. So far I'm dealing with them by calling (.-id tempid) and using a uuid regex in my bidi routes to handle them.
I tried extending the ParameterEncoding protocol but it results in an error. Is there a way to do this?
(def id+
"Matches both a uuid and a datoic db/id."
#"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|-?\d+")
(extend-protocol bidi/ParameterEncoding
om.tempid/TempId
(bidi.bidi/encode-parameter [tempid]
(str (.-id tempid))))
(defn tempid [id]
(om.next/tempid id))
(def routes
["/"
{
"dashboards/" {"" :dashboards/index
"new" :dashboards/new
[[id+ :id] "/edit"] :dashboards/edit
[[tempid :id] "/edit"] :dashboards/edit2
}
"" [[true :dashboards/index]]}])
(bidi.bidi/path-for routes :dashboards/edit :id (.-id (om.next/tempid)))
=> "/dashboards/eaf9396e-c840-4a70-aa68-36e7b8bc6177/edit"
(bidi.bidi/path-for routes :dashboards/edit2 :id (om.next/tempid))
=> #object[Error Error: No matching clause: function arc$router$tempid(id)
Update, after looking at the bidi source code I don't think bidi supports custom types in the routes. Is that correct?
If so, what would it take to make bidi more extensible? Any desire to do this?
I was able to make it work like this. But I would love for the function types to be more extensible rather than monkey-patching bidi. The problem IMO is that the bidi/PatternSegment protocol for function is not extensible. I think this could be addressed by using multimethods in the bodies of the function type protocol methods.
If there is interest in doing this I would be happy to submit a PR.
(ns arc.router
(:require
[om.next :as om]
[bidi.bidi :as bidi]))
(extend-protocol bidi/PatternSegment
function
(segment-regex-group [this]
(condp = this
keyword "[A-Za-z]+[A-Za-z0-9\\*\\+\\!\\-\\_\\?\\.]*(?:%2F[A-Za-z]+[A-Za-z0-9\\*\\+\\!\\-\\_\\?\\.]*)?"
long "-?\\d{1,19}"
bidi/uuid "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}"
om/tempid "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}"
:otherwise (throw (ex-info (str "Unidentified function qualifier to pattern segment: " this) {}))))
(transform-param [this]
(condp = this
;; keyword is close, but must be applied to a decoded string, to work with namespaced keywords
keyword (comp keyword bidi/url-decode)
long #(js/Number %)
bidi/uuid bidi/uuid
om/tempid om/tempid
(throw (ex-info (str "Unrecognized function " this) {}))))
(matches? [this s]
(condp = this
keyword (keyword? s)
long (not (js/isNaN s))
bidi/uuid (instance? cljs.core.UUID s)
om/tempid (om/tempid? s))))
(def routes
["/"
{"dashboards/" {[[long :id] "/edit"] :dashboards/edit
[[bidi/uuid :id] "/edit2"] :dashboards/edit2
[[om/tempid :id] "/edit3"] :dashboards/edit3}
"" [[true :dashboards/index]]}])
;; Long
(bidi.bidi/path-for routes :dashboards/edit :id 34)
=> "/dashboards/34/edit"
(bidi.bidi/match-route routes "/dashboards/34/edit")
=> {:route-params {:id 34}, :handler :dashboards/edit}
;; UUID
(bidi/path-for routes :dashboards/edit2 :id (random-uuid))
=> "/dashboards/0079f19d-2a52-476a-a454-b9a28aae1d7c/edit2"
(bidi.bidi/match-route routes "/dashboards/e544018a-e3c8-4123-b555-fdf66336eb0e/edit2")
=> {:route-params {:id #uuid "e544018a-e3c8-4123-b555-fdf66336eb0e"}, :handler :dashboards/edit2}
;; om.next/tempid
(bidi/path-for routes :dashboards/edit3 :id (om/tempid))
=> "/dashboards/64033318-13e0-4683-9475-e9743589bd73/edit3"
(bidi/match-route routes "/dashboards/e544018a-e3c8-4123-b555-fdf66336eb0e/edit3")
=> {:route-params {:id #om/id["e544018a-e3c8-4123-b555-fdf66336eb0e"]}, :handler :dashboards/edit3}