match a route in Compojure / Clout - regex

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)

Related

Resolving a keyword into a Malli schema from the default registry in Clojure

How do I resolve a keyword to a schema from the default Malli registry? I seem unable to look up a value in the registry in order to walk it.
(def registry
(atom {}))
(defn register! [type ?schema]
(swap! registry assoc type ?schema))
;; Combine the default registry with our own mutable registry.
(mreg/set-default-registry!
(mreg/composite-registry
(mreg/fast-registry (malli/default-schemas))
(mreg/mutable-registry registry)))
(register! :db/kasse
[:map
[:id [:int {:primary-key true :db-generated true}]]
[:odlingsplats [:string {:foreign-key "odlingsplatser"}]]
[:diameter_m :int]
[:djup_m :int]
[:volym_m2 [:int {:db-generated true}]]])
(malli/walk
:db/kasse
(malli/schema-walker identity))
;; => :db/kasse
I've tried wrapping :db/kasse in different functions from malli but none seem to do the lookup and malli/-lookup is private. Just running (:db/kasse malli/default-registry) does not work either. Using malli/schema seems like the obvious choice but it seemingly has no effect.
(malli/walk
(malli/schema :db/kasse)
(malli/schema-walker identity))
;; => :db/kasse
Calling malli/deref was the answer:
(malli/walk
(malli/deref :db/kasse)
(malli/schema-walker identity))
;; => [:map [:id [:int {:primary-key true, :db-generated true}]] [:odlingsplats [:postgres/string {:foreign-key "odli\
ngsplatser"}]] [:diameter_m :int] [:djup_m :int] [:volym_m2 [:int {:db-generated true}]] [:namn {:optional true} [:po\
stgres/string {:db-generated true}]]]
Thank you to ikitommi at the Clojurians slack for providing the answer. He also provided an explanation as to why the library works this way:
The :db/kasse returned is a Malli Schema instance, it’s print output is just the form, so looks like keyword. It’s type is :malli.core/schema, which is the internal eager reference, like a Var in Clojure. If you want to get the schema behind it, you can m/deref it. But, calling m/validate on :db/kasse works too. the :malli.core/schema forwards the calls to the actual instance, like Var.

Testing Timbre log outputs with Midje

I am using Timbre as a logging library but I am having issues testing it with Midje. I've got the following method and all I'm trying to do is asserting that Will be printed is printed.
(defn init [level]
(timbre/merge-config! {:output-fn my-output-fn})
(timbre/set-level! level)
(timbre/info "Will be printed")
(timbre/debug "Won't be printed"))
By wrapping (timbre/default-output-fn) around a function of my own my-output-fn, I am able to assert that only 1 out of 2 logs are printed, which is true:
(fact "print info but not debug"
(core/init :info) => nil
(provided
(core/my-output-fn anything) => irrelevant :times 1))
However I would like to make sure that the message being printed is Will be printed. I can't find any way of doing this, what would you do?
I am doing experiments in the following project https://github.com/stephanebruckert/timbre-midje
It is possible to validate the input of a function using Midje's as-checker prerequisite.
When you print the data that come into timbre/default-output-fn (I discovered this via your output-fn) you see it has the following structure:
{:hash_ #delay[{:status :pending, :val nil} 0x5ed805b1], :instant #inst "2016-10-14T17:07:16.779-00:00", :config {:level :info, ....
So the log-level is available in data. With as-checker the log level can be validated as follows:
(fact "print info but not debug"
(core/init :info) => nil
(provided
(timbre/default-output-fn anything ; opts is first argument
(as-checker (fn [data]
(= (-> data :config :level) ; get log level
:info)))) ; validate that it is info
=> anything :times 1)
(provided
(timbre/default-output-fn anything
(as-checker (fn [data]
(= (-> data :config :level)
:debug))))
=> anything :times 0))
Now, you wanted to check the content of the message. Not only the log level.
The actual message is not available in the opts or data passed toward default-output-fn. Looking around in the the Timbre code I saw it is available in a private function vargs->margs that takes an ?err, a msg-type and vargs. The vargs contain the message (in your case what comes in is for example :auto :p ["The message"]).
Via the as-checker approach the messages can be validated follows:
(fact "print Will be printed, not Won't be printed"
(core/init :info) => nil
(provided
(#'timbre/vargs->margs anything anything ["Will be printed"]) => {} :times 1)
(provided
(#'timbre/vargs->margs anything anything ["Won't be printed"]) => {} :times 0) )
Note that vargs->margs has to return a map, otherwise Timbre throws an exception in a later function.
This way it is validated that "Will be printed" is printed once, and "Won't be printed" is never printed.
#ErwinRooijakkers' idea worked but #ptaoussanis on Github has a good reason not to do it.
Please note that timbre/vargs->margs is private, and an implementation
detail. Its behaviour can change at any time without notice, would
strongly recommend not depending on it in any way.
It'd be possible to use a custom appender that sends output somewhere that are handy for your tests to analyse. You could setup these appenders as part of your test setup, and/or use something like timbre/with-config for the appropriate test calls.
So we can add an appender that passes the parameters to check (level message) to a stub.
core.clj
(defn init
([level]
(init level {}))
([level config]
(timbre/merge-config! config)
(timbre/set-level! level)
(timbre/info "will be printed")
(timbre/debug "won't be printed")))
core_test.clj
(:require [timbre-midje.core :as core]
[midje.sweet :refer :all]))
(defn log-stub [level message])
(def log-stub-appender
{:appenders
{:test-appender
{:enabled? true
:fn (fn [data] (log-stub (:level data) (:vargs data)))}}})
(fact "print info but not debug"
(core/init :info log-stub-appender) => nil
(provided
(log-stub :info ["will be printed"]) => irrelevant :times 1
(log-stub :debug ["won't be printed"]) => irrelevant :times 0))

Use an empty path parameter in nested Compojure routes

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?

Clojure - avoid duplicated code with monger requests

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)

using clojurescript with jquery?

Here is what i am trying to do, I can replace Dom with jQuery, but I don't know how to get the Dom value. That's shame.
Gist
This is a gist which I followed it.
So I ask the origin author, but no response yet.
PS: I just want to try some cljs in browser, so I didn't use any lib like jayq.
I have tried something like (.val "yes"), but it seems to be wrong.
(ns hello-world.jquerytest)
(enable-console-print!)
(def jquery (js* "$"))
(defn x []
(->
(jquery ".meat")
(.html "yes")))
(jquery
(fn []
(x)
(-> (jquery "li.numbers")
(.html "pink")
(.append "banana"))))
Here is my answer based on Vanessa's comment
I tried
(defn x []
(->
(jquery ".meat")
(.html)
(println)))
It will print the value which I want.
If I am using doto
It will print #<[object Object]> in console which I do not want.
(defn x []
(->
(doto
(jquery ".meat")
(.html)
(println))))