I've read somewhere that cljs.reader/read-string attaches metadata to the forms that it creates, like the position in the string read.
Is it true? Is it documented somewhere?
Thanks.
read-string doesn't add metadata to the returned form:
=> (meta (cljs.reader/read-string "(prn 0)"))
nil
Your compiled functions/defs/vars will have this type of metadata though:
=> (meta #'my-fn)
{:ns app.core,
:name my-fn,
:file "src/cljs/app/core.cljs",
:end-column 20,
:column 1,
:line 125,
:end-line 125,
:arglists ([{:keys [x]}]),
:doc nil,
:test nil}
I don't know about cljs.reader, but if you use clojure.tools.reader, you can do this. It's not particularly well documented, but you can see how by looking at the tests: https://github.com/clojure/tools.reader/blob/master/src/test/cljs/cljs/tools/metadata_test.cljs#L62-L70
In short, you have to pass the string to clojure.tools.reader.reader-types/indexing-push-back-reader, and from there to clojure.tools.reader/read. (In the test/example above, they first pass to reader-types/string-push-back-reader, but this doesn't appear to be strictly necessary).
Related
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.
Here's my clojure data:
{:local/contacts-capability contacts-capability}
pr-str gives me this:
#:local{:contacts-capability #uuid "00000000-0000-4000-8000-000000000003"}
I pass this over to clojurescript and when I read it, I get:
Uncaught Error: Could not find tag parser for :local in ("simpleArk.arkRecord.Ark-record" "uuid/Timestamp" "inst" "js" "queue" "uuid" "miMap/MI-map" "tailrecursion.priority-map" "simpleArk.rolonRecord.Rolon-record")
at Function.cljs.reader.reader_error.cljs$core$IFn$_invoke$arity$variadic (reader.cljs:71)
at cljs$reader$reader_error (reader.cljs:69)
at cljs$reader$maybe_read_tagged_type (reader.cljs:613)
at cljs$reader$read_dispatch (reader.cljs:260)
at cljs$reader$read_delimited_list (reader.cljs:233)
at cljs$reader$read_vector (reader.cljs:280)
at cljs$reader$read (reader.cljs:464)
at cljs$reader$read_string (reader.cljs:477)
at console$client$display_property (client.cljs:366)
at console$client$explore_BANG_ (client.cljs:404)
Dependencies:
[org.clojure/clojure "1.9.0-alpha10" :scope "provided"]
[org.clojure/clojurescript "1.9.198"]
In Clojure 1.9, these two maps represent the same data:
#:foo{:bar 1 :baz 2}
{:foo/bar 1 :foo/baz 2}
If all the keys in the map are keywords and have the same namespace, the former is a shorter way to represent the latter. See here: http://dev.clojure.org/jira/browse/CLJ-1910
I suspect that this is currently not supported in ClojureScript.
The problem is with using such an old version of clojure script. Later versions do fix this. See https://github.com/ptaoussanis/sente/issues/241
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))
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)