Is this a known pr-str/keyword bug in clojure? - clojure

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

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.

How to generate JWT exp claim with java-time?

Most examples for JWT token use clj-time which is now deprecated in favor of native java.time. I'm trying to use java-time along with buddy to sign/verify tokens but I'm stuck trying to pass the exp claim to my token. Here's an example of what I have:
(ns test-app.test-ns
(:require
[buddy.sign.jwt :as jwt]
[buddy.auth.backends.token :as bat]
[java-time :as t]))
(def secret "myfishysecret")
(def auth-backend (bat/jws-backend {:secret secret
:options {:alg :hs512}}))
(def token (jwt/sign {:user "slacker"
:exp (t/plus (t/local-date-time) (t/seconds 60))
} secret {:alg :hs512}))
When tyring to test if I can unsign the token
(jwt/unsign token secret {:alg :hs512})
I get the following error:
Execution error (JsonGenerationException) at
cheshire.generate/generate (generate.clj:152). Cannot JSON encode
object of class: class java.time.LocalDateTime:
2021-01-22T12:37:52.206456
So, I tried to pass the same by encapsulating the call to (t/plus ...) inside a (str) but then I get this error:
class java.lang.String cannot be cast to class java.lang.Number
(java.lang.String and java.lang.Number are in module java.base of
loader 'bootstrap')
So, I'm stuck since I don't really know how to generate a valid exp number value using java-time (according to this question, format should be in seconds since the epoch). Older examples using clj-time just passed the exp claim value as
(clj-time.core/plus (clj-time.core/now) (clj-time.core/seconds 3600))
Any help is highly appreciated.
EDIT: Alan Thompson's answer works perfectly, for what's worth this would be the equivalent using the java-time wrapper:
(t/plus (t/instant) (t/seconds 60))
Here are 2 ways to do it:
(let [now+60 (-> (Instant/now)
(.plusSeconds 60))
now+60-unixsecs (.getEpochSecond now+60)]
(jwt/sign {:user "slacker" :exp now+60 } secret {:alg :hs512})
(jwt/sign {:user "slacker" :exp now+60-unixsecs} secret {:alg :hs512}))
and we have the now results:
now+60 => <#java.time.Instant #object[java.time.Instant 0x7ce0054c "2021-01-22T19:04:51.905586442Z"]>
now+60-unixsecs => <#java.lang.Long 1611342291>
So you have your choice of methods. It appears that buddy knows how to convert from a java.time.Instant, so going all the way to unix-seconds is unnecessary.
You may also be interested in this library of helper and convenience functions for working with java.time.

Does reader/read-string attach metadata to the forms

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).

match a route in Compojure / Clout

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)

Clojure: Attaching annotations to AOT-compiled methods

I have a module for the BaseX Java interface which I'm writing in Clojure. The interface provides a number of annotations which can be used to determine how methods are called and optimized; however, I'm having trouble getting these to actually attach to the generated class:
(ns net.dyfis.svnkit_wrapper.SvnWrapper
(:import (org.basex.query QueryModule
QueryModule$Requires
QueryModule$Permission
QueryModule$Deterministic))
(:gen-class
:main false
:extends org.basex.query.QueryModule
:methods [
^{:static true}
[^{QueryModule$Requires QueryModule$Permission/NONE,
Deprecated {}}
cat [java.lang.String] java.lang.String]
^{:static true}
[^{QueryModule$Deterministic {},
QueryModule$Requires QueryModule$Permission/NONE}
catRev [java.lang.String int] java.lang.String]]))
However, only the Deprecated annotation gets attached -- the QueryModule$Requires and QueryModule$Deterministic annotations are silently discarded:
>>> cat
public static java.lang.String net.dyfis.svnkit_wrapper.SvnWrapper.cat(java.lang.String)
>>> cat.getAnnotations()
array(java.lang.annotation.Annotation,[#java.lang.Deprecated()])
This is happening with Clojure 1.4.0-beta6, whereas support for annotations in AOT-compiled methods is supposed to be present from Clojure 1.2. As such, this is presumably a usage error -- but what should I be doing differently?
Except for classes in java.lang (like Deprecated), all classnames must be fully qualified in gen-class declarations. So, your code should be:
^{org.basex.query.QueryModule$Deterministic {},
org.basex.query.QueryModule$Requires org.basex.query.QueryModule$Permission/NONE}
Note that the same restriction does not apply to annotation metadata on/in deftype, defprotocol, or defrecord forms.
Moving the gen-class definition out of the ns declaration allowed the imports to apply:
(ns net.dyfis.svnkit_wrapper.SvnWrapper
(:import (org.basex.query QueryModule
QueryModule$Requires
QueryModule$Permission
QueryModule$Deterministic)))
(gen-class
:name com.indeed.svnkit_wrapper.SvnWrapper
:main false
:extends org.basex.query.QueryModule
:methods [
^{:static true}
[^{QueryModule$Requires QueryModule$Permission/NONE,
Deprecated {}}
cat [java.lang.String] java.lang.String]
^{:static true}
[^{QueryModule$Deterministic {},
QueryModule$Requires QueryModule$Permission/NONE}
catRev [java.lang.String long] java.lang.String]
])