Fork me on GitHub
#beginners
<
2023-01-07
>
Drew Verlee16:01:17

What is the correct way to call compojure? Here is what i tried and it thows:

(require '[compojure.core :refer :all])

  (defroutes myapp
    (GET "/" [] "Hello World"))

  (myapp {:uri "/" :request-method :get})

Drew Verlee16:01:49

i'm starting to get the impression you can't test your compojure routes from the repl

practicalli-johnny17:01:00

Maybe https://github.com/ring-clojure/ring-mock can be used

(mock/request :get "/")
Although I assume it's doing something similar.

👀 1
jumar19:01:52

I’m not sure I ever tried that. I always have a separate function that implements the route handler and I can easily call and debug that function instead

👍 2
Drew Verlee06:01:12

Thanks I'll think on that. My issue is that it's not clear where in the request response transformation the issue is. It's not in the routing, but the router seems to wrap the middle ware. So I'm not sure how to not include it as the entry point.

teodorlu19:01:10

I'd use the REPL! Compojure routes can be given the raw request if you ask for it.

(defonce last-req (atom nil))

(defn handle [req]
  (reset! last-req req)
  "Hello, World")

(defroutes app
  (GET "/" req (handle req)))
Then evaluate @last-req from the REPL. And you should be able to call your router with that data. (app @last-req).

Drew Verlee20:01:58

@U3X7174KS thanks for the suggestion. Do you mind if i share a bit about whats going on here? I'm going to dive into the details and how i'm thinking about them. Its going to be a bit of rant because the issues are only surfaced in real time as i'm trying to debug something. As in, once i know the solution, just plugging it in place isn't really interesting, its how i learned what i had to do and my goal is to improve the process. <rant> I'm trying to use the repl. But it's not clear to me what the best approach is. That is, it's not clear what my easy options are for breaking the problem into small chunks are. To address your comment direclty, i'm not sure the issue is in my handler OR the middleware around it. And trying to get an entry point that walks through both the handle and the middleware is currently a challenge to me and i want to understand both how to solve the problem and why its challenging in the first place. The problem is when we hit the 2022-06-06 endpoint (see below) with a http request the response isn't what we expect. The details are important really. Here are the routes:

(compojure/context
                       "/graphql"
                       _
                       (-> (compojure/routes
                            (compojure/POST "/2022-06-06" _ (request-handler this)))
                           middleware/wrap-keywordize-headers
                           (ring-cors/wrap-cors :access-control-allow-origin [#".*"
                                                                                #"localhost"
                                                                                #".*\.mycentriq\.com"
                                                                                #".*\.centriqhome\.com"]
                                                  :access-control-allow-methods [:post])
                           ring-json/wrap-json-response
                           ring-json/wrap-json-body
                           ring-gzip/wrap-gzip))
My first step was to make sure i could make small changes to the system and they would happen. As you put it, use the repl. so i did this:
(compojure/context
    "/graphql"
    _
    (-> (compojure/routes
          (compojure/POST "/2022-06-06" _ "HELLO WORLD"))
))
then i ran (reloaded.repl/reset) so my server state and clojure namespaces would be reset and the handler and middlware would update. This lets me do the full end to end test with an http call so i'm sure i'm in the right context. But the new http request didn't return "Hello world" it returned the old incorrect value. So my expectations weren't meet and as of right now, I still don't know why. The rest of this thread has context on that if your intrested. But because i have to fix this in a timely matter i decided to abandon reloading my application state and just focused on isolated functions. So what i know it's the graphql endpoint, and i know it's the /2022-06-06 endpoint route. So because i value building blocks i assume i can call the return value of this compojure context like a function like so:
(def context
    (compojure/context
      "/graphql"
      _
      (-> (compojure/routes
           (compojure/POST "/2022-06-06" _ "HELLO WORLD")))))

(context <some args here like {:uri graphql/2022-06-06" ...}>)
Only, i don't think you can. Which is a problem because it's not clear if the issue is in my handler, what "Hello world" is now, or whats called request-handler above OR in the middleware. If i look at the doctsring for context it doesn't tell me that i can call it like a function:
"Give all routes in the form a common path prefix and set of bindings.

  The following example demonstrates defining two routes with a common
  path prefix ('/user/:id') and a common binding ('id'):

      (context \"/user/:id\" [id]
        (GET \"/profile\" [] ...)
        (GET \"/settings\" [] ...))"
So now i have to check either how it's called in our app (which could be messy given the dynamic nature of clojure or i can search the web. I'm i wrong in thinking this feels harder then it needs to be? Ok so how about compojure/routes thats passed to the middleware. Surely it will tell me how you can call it to send it intput and get output. lets look at the docstring:
compojure.core/routes [& handlers]

Create a Ring handler by combining several handlers into one.
Ok, well maybe ill find examples online? here is one of the top results for learn compojure https://learnxinyminutes.com/docs/compojure/ Ahh the blog tells me that I can call 'defroutes" as a function like this:
(myapp {:uri "/" :request-method :post})
; => {:status 200
;     :headers {"Content-Type" "text/html; charset=utf-8}
;     :body "Create Something"}
so maybe that works for compojure routes?
(def r (compojure/routes
          (compojure/POST "/2022-06-06" _ "HELLO WORLD")))

  (r {})
  ;; => nil

  (r {:uri "/"})
  ;; => nil

  (r {:uri "/2022-06-06" :request-method :post}) => throws exception. hmm may
hmm maybe defroutes doesnt work like routes. lets do the example in the blog exactly as is:
(require '[compojure.core :refer :all])

  (defroutes myapp
    (GET "/" [] "Show something")
    (POST "/" [] "Create something")
    (PUT "/" [] "Replace something")
    (PATCH "/" [] "Modify Something")
    (DELETE "/" [] "Annihilate something")
    (OPTIONS "/" [] "Appease something")
    (HEAD "/" [] "Preview something"))

  (myapp {:uri "/" :request-method :post})
That... throws the same exception which looks like:
1. Unhandled java.lang.IllegalArgumentException
   No implementation of method: :route-matches of protocol: #'clout.core/Route
   found for class: clojure.lang.PersistentArrayMap
So my next step is to... idk google this stack trace and i get this https://github.com/weavejester/clout/issues/34 So i'll stop this journey here for now. My point is, is this kind of journey atypical for you? I feel like this kind of frustration happens all the time for me. Like the whole joy of clojure is that you can work with small pieces but i often feel like i run into situations where: a) having my repl break down so things dont get reloaded b) unable to pull things apart c) unable to find places where there is a function that actually takes input </rant> If you manged to read this, thanks, you have any feedback i would love to hear it.

teodorlu20:01:09

I feel your pain! I've gotten myself into similar messes myself. To me, it sounds like you're on track digging into what's supposed to happen with your simple example - where you create the simple route table with defroutes. I'd perhaps try: 1. Continuing to dig into the docs - perhaps you'll find that you're having a faulty assumption 2. Perhaps there's some old REPL state sticking around. I'm not in front of a PC right now, so I'm not able to try to reproduce your small example that crashes. I would also expect that example to work.

teodorlu21:01:41

I tried to reproduce your example, but in my case (with compojure 1.7.0), I'm not getting any errors, it seems to give the expected behavior.

(require '[compojure.core :refer :all])

(defroutes myapp
  (GET "/" [] "Hello World"))

(myapp {:uri "/" :request-method :get})
;; => {:status 200, :headers {"Content-Type" "text/html; charset=utf-8"}, :body "Hello World"}
Source: https://github.com/teodorlu/play.teod.eu/tree/2ebfc6500d6102591f41263bdcac3940e3562aa3/code-sandbox/drew-repl/src/drew/repl.clj

Drew Verlee01:01:02

thats very useful information thanks @U3X7174KS i appreciate you looking into it.

Drew Verlee01:01:31

ugh. i still get the exepction with your same setup. wtf could that mean?

Drew Verlee01:01:24

;; Connected to nREPL server - 
;; CIDER 1.6.0 (Buenos Aires), nREPL 1.0.0
;; Clojure 1.11.1, Java 17.0.5

Drew Verlee01:01:12

double ugh it was clojure enlightment mode

Drew Verlee02:01:13

https://github.com/clojure-emacs/cider-nrepl/issues/764 Or at least, thats what i'm seeing. I stand ready to be corrected.

jumar05:01:20

That's what I just wanted to say. I have no problems evaluating the routes themselves. As the defroutes docstring says:

(defmacro defroutes
  "Define a Ring handler function from a sequence of routes. The name may
  optionally be followed by a doc-string and metadata map."
  [name & routes]
Then you look at Ring spec: https://github.com/ring-clojure/ring/blob/master/SPEC > A synchronous handler takes 1 argument, a request map, and returns a response > map. It's also a good idea to look at the request spec: https://github.com/ring-clojure/ring/blob/master/SPEC#L44 -> request has many attributes so I think it's the best to utilize ring.mock.request
(defroutes routes
  (context "/projects" req
    (GET "/" [] (show-projects-page req))
...

(routes (ring.mock.request/request :get "/projects"))

jumar05:01:59

And I have no problems with cider enlighten mode

Drew Verlee18:01:50

@U06BE1L6T concerning enlighten mode, if i opened an issue which has a link to a setup project fails for me. You can give it a try if you want to see if it happens for you. https://github.com/clojure-emacs/cider-nrepl/issues/764

Drew Verlee18:01:45

Also that mode will often result in highlights which wont go away unless i manually clear them. As in, re-evaling with it turned off doesn't remove the highlights. So for what ever reason, in my setup, it's been a bit of a headache. I get the impression not many people use it, so it might not be getting a lot of feedback from various environments. Do you use an emacs framework like spacemacs or doom by any chance?

jumar07:01:28

I use spacemacs and I do not typically use enlighten mode. I find cider debugger and inspector way more useful and those are my go-to tools.

jumar07:01:43

You could try to debug compojure.core.route-matches to understand what's the difference between the invocations. It seems that it's passing, for some reason, a map while route-matches probably expects something else.

Luciano Laratelli22:01:38

Hi folks, I’m trying to compile cljs to node for a digitalocean serverless function. I can compile it correctly and run it with node on my local machine as per the https://clojurescript.org/guides/quick-start, but when I deploy it to DO and run it, I see this error:

2023-01-07T22:36:59.490197493Z stdout: Error: Cannot find module '/tmp/rk0V7iVH/.cljs_node_repl/goog/bootstrap/nodejs.js'
2023-01-07T22:36:59.490269278Z stdout: Require stack:
2023-01-07T22:36:59.490276118Z stdout: - /tmp/rk0V7iVH/validate.js
2023-01-07T22:36:59.490280037Z stdout:     at Function.Module._resolveFilename (internal/modules/cjs/loader.js:902:15)
2023-01-07T22:36:59.490283602Z stdout:     at Function.Module._load (internal/modules/cjs/loader.js:746:27)
2023-01-07T22:36:59.490287097Z stdout:     at Module.require (internal/modules/cjs/loader.js:974:19)
2023-01-07T22:36:59.49029055Z  stdout:     at require (internal/modules/cjs/helpers.js:101:18)
2023-01-07T22:36:59.490294019Z stdout:     at Object.<anonymous> (/tmp/rk0V7iVH/validate.js:19:1)
2023-01-07T22:36:59.490299538Z stdout:     at Module._compile (internal/modules/cjs/loader.js:1085:14)
2023-01-07T22:36:59.490305027Z stdout:     at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
2023-01-07T22:36:59.490309501Z stdout:     at Module.load (internal/modules/cjs/loader.js:950:32)
2023-01-07T22:36:59.490314094Z stdout:     at Function.Module._load (internal/modules/cjs/loader.js:790:12)
2023-01-07T22:36:59.490318703Z stdout:     at ModuleWrap.<anonymous> (internal/modules/esm/translators.js:199:29) {
2023-01-07T22:36:59.490323993Z stdout:   code: 'MODULE_NOT_FOUND',
2023-01-07T22:36:59.490328886Z stdout:   requireStack: [ '/tmp/rk0V7iVH/validate.js' ]
2023-01-07T22:36:59.490333783Z stdout: }
Am I missing a compiler flag to include that module? I’m building with clj -M -m cljs.main --target nodejs --output-to packages/keypad-hack/validate/validate.js -c keypad-hack.validate