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.
Related
I want to test my Compojure endpoints and see that the required method is called. However, with-redefs-fn is not replacing the method.
Test method-
(ns project-name.handler-test
(:require [clojure.test :refer :all]
[project-name.handler :as handler :refer [retrieve-records test-handler]]
[ring.mock.request :as mock]))
(deftest fn-handler-routes
(with-redefs-fn {#'handler/retrieve-records (fn [arg] :test)}
(let [response {:status 200 :body :test}]
(let [mock-handler (test-handler)]
(is (= response (mock-handler (mock/request :get "/records/name"))))))))
I am guessing routes are static, and thus the method fires and returns actual data instead of calling the redefined function. test-handler is a failing attempt to try to circumvent -- it fires okay, but does not use with-redefs and returns repo data.
Pertinent Source Code-
(defn retrieve-records [arg] (repo/get-records arg))
(defroutes routes
(GET "/" [] (resource-response "index.html" {:root "public"}))
(context "/records" []
(GET "/name" [] (retrieve-records :lastname)))
(resources "/"))
(defn test-handler []
(-> #'routes wrap-reload))
Related Issues
I suspect a similar underlying issue as this SO question about Midje but I am using clojure.test and not midje.
This question is different from a very similar SO question in that the Compojure route is legitimate and I want to return something in the :body, not just test the status (already tried that answer).
This question is different from an integration test question in that it is a handler unit test, though probably the same underlying issue.
Any help would be greatly appreciated.
I am using Clojure and Monger
It works fine, and I group functions by the collection they relate to.
Therefore, every file begins like this :
(ns img-cli.model.mycollectionname
(:require [monger.core :as mg]
[monger.collection :as mc]
[edn-config.core :refer [env]])
(:import [com.mongodb MongoOptions ServerAddress DB WriteConcern]
[org.bson.types ObjectId]))
(def config (get-in env [:mongo]))
;; using MongoOptions allows fine-tuning connection parameters,
;; like automatic reconnection (highly recommended for production
;; environment)
(def ^MongoOptions opts (mg/mongo-options { :threads-allowed-to-block-for-connection-multiplier 300}))
(def ^ServerAddress sa (mg/server-address (:url config) (:port config)))
(def conn (mg/connect sa opts))
(def db (mg/get-db conn (:db config)))
(def collection-name "asset")
;; I have to write one like this every time
(defn find-one-as-map
"fetch asset by Id"
[^String id]
(mc/find-one-as-map db collection-name {:_id (ObjectId. id)}))
Code duplication has of course several disadvantages in itself.
Also I'm not sure if connections are properly pooled afterwards ?
How can I avoid doing this ?
I sense I could pass an additional "db" parameter to each function, but then where would it come from ?
If I create the db connection in the "entry" file of my program, then how could it be passed to every function from there ?
For instance let's says I have Compojure routes in different files :
;; in the main handler file
(def db ...) ;; if I move the previous db configuration
;; in here, it could be the only place where this is set
;; importing Compojure routes from different files
(defroutes routes-from-file1
routes-from-file2...)
Let's say that some functions called from some of the routes in "file2" need access to the db, how can I pass this variable to them ?
I also have a lot of repetitive code afterwards, for instance to get data by Id for every collection...
I feel this could be simplified, but I'm not sure how.
Just refer to it by its namespace
(ns foo
(:require [handler :as h]))
(println h/db)
I've created a new Compojure Leiningen project using lein new compojure test. Web server is run by lein repl and then
user=> (use 'ring.adapter.jetty)
user=> (run-jetty test.handler/app {:port 3000})
Routes and app handler specification is trivial:
(defroutes app-routes
(GET "/*.do" [] "Dynamic page")
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
Now, after changing anything in app-routes definition (e.g. changing "Dynamic page" text to anything else, or modifying URI matching string), i do not get the updated text/routes in the browser. But, when changing app-routes definition slightly to
(defn dynfn [] "Dynamic page fn")
(defroutes app-routes
(GET "/*.do" [] (dynfn))
(route/not-found "Not Found"))
i do get dynamic updates when changing the return value of dynfn. Also, following the advice from this article and modifying the app definition to
(def app
(wrap-defaults #'app-routes site-defaults))
(note the #' that transparently creates a var for app-routes) also helps!
Why is that so? Is there any other way one could get a truly dynamic behaviour in defroutes?
Thanks!
#'app-routes is a reader macro that expands to (var app-routes). When a var is used as if it were a function, it is dereferenced anew on each invocation, and then the value returned by that deref is called.
If you were to supply app-routes as the argument, the compiler would give the dereferenced value to wrap-defaults, and when the var is updated, the previous value is not changed, so changing the var does not change the behavior of app.
The following repl transcript might be instructive:
user=> (defn foo [] "original")
#'user/foo
user=> (defn caller [f] #(f))
#'user/caller
user=> (def call-foo-value (caller foo))
#'user/call-foo-value
user=> (call-foo-value)
"original"
user=> (def call-foo-var (caller #'foo))
#'user/call-foo-var
user=> (call-foo-var)
"original"
user=> (defn foo [] "changed")
#'user/foo
user=> (call-foo-value)
"original"
user=> (call-foo-var)
"changed"
I'm trying to use ring-json's wrap-json-response middleware within my compojure app. I have a simple GET handler that returns a map, like {:foo 1}, and when I hit the URL, ring responds with text/plain and an empty response body. I can't seem to get it to respond with the JSON version of the map.
Here's my handler code:
(ns localshop.handler
(:use compojure.core)
(:require [localshop.routes.api.items :as routes-api-items]
[ring.middleware.json :as middleware]
[compojure.handler :as handler]
[compojure.route :as route]))
;; map the route handlers
(defroutes app-routes
(context "/api/item" [] routes-api-items/routes))
;; define the ring application
(def app
(-> (handler/api app-routes)
(middleware/wrap-json-body)
(middleware/wrap-json-params)
(middleware/wrap-json-response)))
The route handler function literally just returns a map, so the code for that is simple enough that I think I could leave out. If returning a map from a compojure route handler is the problem, then perhaps that's it?
Check out this. Basically if you return {:body {:my-map "hello"}} then it will work fine.
Stumbe similar issue, when writing REST API.
When handler return vector, i get exception that no implementation of method render for PersistentVector in Renderable protocol in compojure.
When return map, headers is empty.
When return sequence, i get 'text/html'.
So, i think it's good to be extend Renderable in our code: really nice gift from clojure.
But, as hack, for fast solution, i use next middleware:
(defn wrap-content-json [h]
(fn [req] (assoc-in (h req) [:headers "Content-Type"] "application/json")))
Below is my app -
(ns mfaiz.routes
(:use [net.cgrand.moustache :only [app]]
[net.cgrand.enlive-html :only [deftemplate content]]
[ring.util.response :only [response]]))
(deftemplate reg "mfaiz/templates/reg.html" [])
(def my-app (app
["hi"] "Hello World!"
["reg"] (-> ((-> "reg" symbol resolve)) response constantly)
[&] "Nothing was found"))
I encounter error with this route -
["reg"] (-> ((-> "reg" symbol resolve)) response constantly)
If I evaluate the above route directly, it works properly and returns the html file -
((-> "reg" symbol resolve))
If I also change the route to directly call the template function then also it works -
["reg"] (-> (reg) response constantly)
Any ideas what is going wrong ?
The problem seems to be the fact that when it run under ring, the "reg" is not being resolved because the it is not fully qualified. It depends in which namespace the ring server is started. So using fully qualified name will work:
(-> "mfaiz.routes/reg" symbol resolve)
Please check resolve documentation. It tries to resolve the symbol in current namespace i.e in *ns*