This routes works in Luminus/Compojure/Ring app
(GET "/page/:id" [id] (home-page id))
but this doesn't and throws an error:
(GET ["/page/:id" :id #"^[1-9]\d{0,2}$"] [id] (home-page id))
The error is "Page not found", even when I go to the same url "page/2"
Remove the anchors ^ $, which are redundant: apparently the keyword (:id) specifies which part of the route string (":id") is to be matched by the regex, in entirety.
The answer to why this is so is likely found in the implementation of this logic, Clout.
Related
So I use re-graph version 0.1.11 and I try fetching data from the endpoint. After fetching data, I checked network tab in the browser and I found the expected data after that it should activate my callback function, but it doesn't seem to work (but sometimes it works and after refreshing the page a few times it doesn't work again). Here is the code.
;; how I init re-graph
(rf/dispatch [::re-graph/init
::conf/graphql-client-name
{:ws-url url
:http-url url
:ws-reconnect-timeout 500
:resume-subscriptions? true}])
(re-frame.core/reg-event-fx
::fetch-expected-data
(fn [cofx event]
(let [app-db (:db cofx)
some-params (-> event second (cljs.core/js->clj :keywordize-keys true))
token (-> app-db (lens/get-in (auth-db/lens-token :group-level-x)))]
(re-frame.core/dispatch
[:re-graph.core/query
::conf/graphql-client-name
"query findExpectedData($query: FetchExpectedDataInput!, $token: String!) {
findExpectedData(query: $query, token: $token){
value1
value2
...
}
}"
{:query some-params
:token token}
;; this is where the problem occurs
;; even though i found the data in the network tab, but
;; this callback doesn't seem to work (sometimes it works sometimes it doens't)
[::fetched-data-completed]]))))
(re-frame.core/reg-event-fx
::fetched-data-completed
(fn [cofx [_ {:keys [data errors] :as payload}]]
(let [app-db (:db cofx)
error-message (-> errors :errors first :message)]
(if (or (nil? errors) (empty? errors))
(do (bla bla when success))
(pr error-message)))))
I'm stuck with this problem for a few months. maybe because I fetch a lot of data at the same time? or could be something else anyone knows?. By the way the actual code I use defmacro, but it works the same way as the above code.
So I managed to find the answer to my own question. It seems like app-db has not been initialized properly so I fixed that problem and everything works fine. hope it helps someone who struggle with this problem.
I'm learning ClojureScript, I have two functions that just change the content in the "root-app" div:
(ns blog.core)
(defn mount-components []
(let [content (js/document.getElementById "root-app")]
(while (.hasChildNodes content)
(.removeChild content (.-lastChild content)))
(.appendChild content (js/document.createTextNode "Wilkommen zu mein
ekelhaft blog!!"))))
(defn init! []
(def current_url js/window.location.href)
(if (clojure.string/includes? current_url "about")
(.log js/console (str "Whatever URL ->>>" js/window.location.href))
(mount-components)))
All works fine in http://localhost:3000/about because the "root-app" div exists in that page, but in http://localhost:3000/blog, I get the error message:
Because there is no such div in that page. All this is weird because it looks like ClojureScript in fact finds that:
(if (clojure.string/includes? current_url "about")
is actually false en the console.log is not printed.
My question is: why the function mount-components runs and sends the error message even when the conditional if is false? The weird thing is that the console.log:
(.log js/console (str "Whatever URL ->>>" js/window.location.href))
doesn't run but mount-components function does. I guess I'm not understanding the "sequence" in the way that ClojureScript works.
I'm not sure, but by your description, I think the logic you are thinking of and the logic you are actually testing are not the same. Your if statement looks for the word 'about' in the URL. If it is there, then it prints the console log i.e. it will be there for http://localhost:300/about. If it is NOT there, it will run the mount-components function, which looks for the div ID which you say is not included on the page, so you get the error. the mount-components is an ELSE statement and is therefore executed when the test is false.
The if form works like (if cond true-branch false-branch), so your (mount-component) is executed because it's in the false branch. Check out when, which only has a true branch.
When running this code :
(:use 'compojure.core)
(keys (ns-publics 'compojure.core))
(defroutes app-routes
(GET "/" [] "Hello World")
(route/resources "/")
(route/not-found "Not Found"))
I got this message:
CompilerException java.lang.RuntimeException: Unable to resolve symbol: defroutes in this context, compiling:(restful_clojure\routes.clj:5:1)
but when I run:
(keys (ns-publics 'compojure.core))
it shows that macro is defined:
(defroutes PUT POST routing routes make-route let-routes DELETE ANY let-request GET HEAD PATCH context OPTIONS)
Clojure has methods require, import, refer, and use. These are for working with different namespaces.
:use is a Keyword, which can behave like a function (in your example it should return nil), but does not do what you want.
The confusion likely arises from the fact that inside the ns macro, you can 'embed' the behavior of these functions using the corresponding keywords.
For more reading on namespaces, see this link.
How to match URLs without a trailing backspace in Compojure nested contexts?
Problem I'm trying to solve:
I want to match the following routes:
/users/
/users/:user-id
/users/:user-id/resources/
The first to access all users,
second for a single user entity,
last for all resources of a given user.
Using the following defroutes works:
(defroutes
(GET "/users/" ...)
(GET "/users/:user-id" ...)
(GET "/users/:user-id/resources/" ...))
However, as my API grow I want to use contexts to clarify the code.
(defroutes
(context "/users"
(users-routes) ;; All users operations
(context "/:user-id" [user-id]
(user-routes user-id) ;; All per-user operations
(context "/resources" []
(resources-routes user-id))))) ;; All user - resources operations
Which means that user-routes should look like:
(defn user-routes [user-id]
(routes
(GET "" [uid] ...)))
Which gives, at compile time:
clojure.lang.ExceptionInfo: Parse error in route string
data: {:failure
{:index 0,
:reason
[{:tag :regexp, :expecting #"(https?:)?//"}
{:tag :string, :expecting ":"}
{:tag :string, :expecting "*"}
{:tag :regexp, :expecting #"\\."}
{:tag :regexp, :expecting #"(:[^\p{L}_*{}\\]|[^:*{}\\])+"}],
:line 1,
:column 1,
:text nil}}
clojure.lang.Compiler$CompilerException: clojure.lang.ExceptionInfo: Parse error in route string {:failure Parse error at line 1, column 1:
nil
^
Expected one of:
#"(https?:)?//"
":"
"*"
#"\\."
#"(:[^\p{L}_*{}\\]|[^:*{}\\])+"
}, compiling:(/Users/laurent/dev/suricate/web/src/web/routes/api.clj:134:5)
I believe this is due to the way urls are parsed (with Clout) hence my question,
What's the correct way to match URLs without a trailing backspace in Compojure nested contexts?
I am trying to match routes of the following form : {{mongoID}}.{{width}}x{{height}}.{{extension}}
For instance, /5591499e2dbc18bd0f000050.240x240.jpegis a valid route.
I'd like to be able to destructure it like so :
{:id 5591499e2dbc18bd0f000050
:width 240
:height 240
:extension jpeg }
Compojure supports regex, and dots too apparently https://github.com/weavejester/compojure/issues/42 .
I can have individual regexes for each of the fields, but I'm not sure how to put that into the route path (I'm trying to use the array syntax) :
https://github.com/weavejester/compojure/wiki/Routes-In-Detail#matching-the-uri
Let's say I have this :
(GET ["/my-route/:mongoID.:widthx:height.:extension" :mongoID ...
:width ...
:height ...
:extension ...])
Obviously the string "/my-route/:mongoID.:widthx:height.:extension" won't work (just because the "x" is lost, maybe something else too).
How can I modify my route to make it match my arguments ?
Note : I'm also using Prismatic/Schema if that's useful.
Compojure uses clout for route matching. That's how it allows you to specify the regex for each parameter. The following works in clout:
user=> (require '[clout.core :as clout])
user=> (require '[ring.mock.request :refer [request]])
user=> (clout/route-matches (clout/route-compile "/my-route/:mongoID.:width{\\d+}x:height{\\d+}.:extension") (request :get "/my-route/5591499e2dbc18bd0f000050.240x240.jpeg"))
{:extension "jpeg", :height "240", :width "240", :mongoID "5591499e2dbc18bd0f000050"}
So the following should work in compojure:
(GET "/my-route/:mongoID.:width{\\d+}x:height{\\d+}.:extension"
[mongoID width height extension]
(do-something-with mongoID width heigth extension)