Is there a way to tell if RT.init() has already been called? RT.INIT boolean is private.
Generally you shouldn’t need to ask that question so maybe back up and explain why you want to
How do you use a list inside a unquote splicing thing? Like if I have a macro, something like this:
(defmacro foo
[entities-list]
(let [es (gensym 'entities)]
`(let [~es ~entities-list
result ~@(for [e ~@es ;;; <- this is wrong. how do I refer to this list?]
(boolean e))]
result)))
~@(for [e ~es]
says: Attempting to call unbound fn: #'clojure.core/unquote
~@(for [e `~es]
says: Don't know how to create ISeq from: clojure.lang.Symbol
~@(for [e `~@es]
says: splice not in list
Can the output of clojure.pprint/pprint
considered EDN?
(of course when it contains some non-clojure with custom print method implementation, then it isn't EDN.)
@ag you can't unquote inside an unquote
in other words, is (clojure.data.edn/read (with-out-str (clojure.pprint/pprint x)))
always does the same as (clojure.data.edn/read (pr-str x)
?
@onetom no it's not necessarily edn, and no it's not necessarily readable
pprint may for example truncate long collections depending on your settings
@ag try just using autogensym inside the syntax quote (no need to unquote then, it's in the emitted code)
`(let [es# ~entities-list
result ~@(for [e es#] ...
damn it.... but... I really want to.
I think I tried that, but let me try again
it can't resolve symbol if done like that
I think, I'm gonna have to write a nested macro call
can't resolve what symbol?
I mean what are you actually trying to do here? do you even need a macro?
eh... probably no, maybe I don't need it. It's a legacy code done with a macro, I'm trying to extend it without breaking it.
I mean, it looks like (first (map boolean entities))
but maybe I'm missing something
eh, no, it has something to do with generating compojure routes, it's a bit hairy. It's alright, I'll figure it out. Thanks Alex!
Compojure routes are ring handlers with the addition of returning nil if the route doesn't match, you definitely don't need a macro to make those
es is not a list, because the for is unquoted, that means the code is evaluated at macro expansion time, not runtime, and es is at runtime a list, but at macro expand time it is a symbol
I know, but how do I generate something like this:
(context
"/foo" []
(context "/bar" []
:tags ["Bar"]
(bar-routes params))
(context "/zap" []
:tags ["Zap"]
(zap-routes params))
,,,
)
dynamically?I can do something like:
(compojure.api.sweet/routes
(compojure.core/make-context
(:route-context entity) ;; something like "/bar"
(:routes entity) ;; => a bunch of compojure.api.core/routes
but I can't figure out how to pass the tagsthere is no reason to bother contexts at all
like, they exist to remove repetition for people writing routes out manually
I dunno what that tags things is, it isn't part of compojure, must be something in that compojure.api.sweet thing
https://github.com/metosin/compojure-api/blob/85738b802c7f150f4f550ffca91a4782c75f8091/examples/reusable-resources/src/example/entity.clj#L34-L73 maybe a useful example
most of what that compojure-api project does, is not compojure at all, which is useful to know when asking for help
which might explain why you are having trouble, truing to use compojure.core/make-context, when you need to use whatever the equivalent in that compojure-api project is
oh, that example I think extremely useful for my case right now. Thanks!
Yeah, I've always sort of wondered about compojure-api because it really isn't compojure at all...
aside from magic and bad-naming of compojure-api, context
in Compojure (and in compojure-api) creates the child routes at reqest-time, which is a surpise for many.
(require '[compojure.core :refer :all])
(defn my-routes []
(println "created routing table")
(GET "/ping" [] (constantly "pong")))
(def app
(context "/foo" []
(context "/bar" []
(my-routes))
(context "/zap" []
(my-routes))))
(app {:request-method :get, :uri "/foo/bar/ping"})
;created routing table
;=> {:status 200, :headers {"Content-Type" "text/html; charset=utf-8"}, :body "pong"}
(app {:request-method :get, :uri "/foo/bar/ping"})
;created routing table
;=> {:status 200, :headers {"Content-Type" "text/html; charset=utf-8"}, :body "pong"}
(actually, c-api has an optimization for it, creates static contexts only once)
if static routing is what you want, here’s valid generated reitit-ring-syntax, no dependencies / macros:
(defn my-routes []
["/ping" {:get (constantly {:status 200, :body "pong"})}])
["/foo"
(for [[name routes] {"bar" (my-routes)
"zap" (my-routes)}]
[(str "/" name) {:swagger {:tags #{name}}} routes])]
;["/foo"
; (["/bar" {:swagger {:tags #{"bar"}}}
; ["/ping" {:get #object[...]}]]
; ["/zap" {:swagger {:tags #{"zap"}}}
; ["/ping" {:get #object[...]}]])]
What is best way to keep a user logged in securely? I know that I can send a session cookie with each request, but now there is a bunch of alternatives (e.g. JWT) available and it’s all a bit confusing to me. Adding to that, is the fact that the ring session middleware seems to be quite hegemonic in the Clojure world, but there is also buddy-auth? I would like to use reitit and it seems to plug in the ring middleware too for session management, but with a caveat: https://github.com/metosin/reitit/issues/205 What are people here doing and what should I watch out for?
You shouldn't use JWT for that: https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid
If you have a persistent data storage on your backend, just use sessions and cookies with session IDs, that's it. With 99.9% certainty you don't need any other solution.
also use https and your session cookie should have secure attribute
What's the secure attribute?
"A cookie with the `Secure` attribute is sent to the server only with an encrypted request over the HTTPS protocol, never with unsecured HTTP" https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
you dont' want your session cookie intercepted midway, use https and ensure that your session cookie transmitted only using https
Right. Thanks a lot! This will of course be https.
if you're using cookies you can set same-site these days which will help prevent csrf attacks, also you'll want to use httponly to stop it being read by malicious javascript injected via cross site scripting in addition to the secure flag
Guess I’ll set all of those. Useful stuff.
Was -X
replaced with -A
in the clojure cli?
i think its largely more along the lines that -A
is being replaced with -X
Here's a macro puzzler for someone: I need to call a macro like this:
(rules-dsl/parse-rule [[Product (= sku "123")]] (prn "hii"))
That works fine. However, the vector part will be stored in a var because I'm loading it from a database, so it will really be more like this:
(let [form '[[Product (= sku "123")]]]
(rules-dsl/parse-rule form (prn "hii")))
This fails because the macro rules-dsl/parse-rule
is needing the expression stored in form
, but actually just gets the symbol form
. I suspect the solution to this is to write a wrapper macro and look up the value of form
in the let
's scope using &env
. However, I can't figure out how to use the clojure.lang.Compiler$LocalBinding
s that &env
provides. Any ideas on how to do this?Don't
Make parse-rule a function
I didn't. It's part of clara-rules. We just need to use it.
(also let bindings don't involve vars)
Right, I forgot. vars are only top level. 👍
Ask in clara-rules for the function equivalent
Which is maybe the dsl/build-rule function
It looks like that works. Thanks @hiredman! Somehow I missed that function. Sorry for the post in the wrong-channel.
I'm still curious though, does anyone know how to pass a form to a macro that's stored in a local binding? Or perhaps how to use the LocalBinding
s in &env
?
-A's use is moving towards being applicable primarily for repl invocation with -M for main invocation and -X for function execution, but for now it can still be used to supply main-opts (like -M)
that particular use case (-A for main execution) is effectively deprecated and will warn (eventually will be turned off)
You can't
where eventually is probably ~months
The value of the binding only exists at runtime, and macros are expanded away already
Because on clojure (and other lisps) the macro language and the regular language are the same it is easy to confuse them
It can be held up to think of the macro language as clojure-0 and the language as clojure-1
think of this example:
(let [x ['y 3]]
(let x (+ y 2)))
wanting to use the value of x
instead of the for [y 3]
as the bindingClojure-0 is a language where all the functions (macros) take as arguments and return clojure-1 expressions
And clojure-1 is what you think of as regular clojure.
It not being possible surprises me. &env
clearly has something in it for form
when called normally, whereas it's nil
when called with macroexpand-1
.
The value of the let binding is a regular clojure expression, not a clojure form
I just can't figure out if the LocalBinding
has what I expect in it or how to use it.
So clojure-0 cannot do anything with it, because clojure-0 only operates on clojure expressions, not the values those expressions evaluate to
&env can tell you a symbol is bound (that is statically known at compile time when macros are expanded) but it can't tell you what value it is bound to, because that value is determined at runtime after the expression has been compiled
but wouldn't you be able to look it up in the expression emitted from the macro?
No
Well, I'm sure you're right--don't mean to imply otherwise. Maybe this is just an opportunity for me to do more research on how the clojure compiler works.
The expression emitted by the macro will be compiled and it can use run-time values when it is executed, yes, but during compile time you do not know those run-time values.
Maybe too trivial of an example, but you can write a very short macro that takes an expression (my-custom-when some-conditional-expr then-expr)
and transforms it into (if some-conditional-expr then-expr)
. At run time, the resulting expression (if some-conditional-expr then-expr)
will evaluate the value of some-conditional-expr
, and at that time decide whether to evaluate then-expr
, or not. While your macro named my-custom-when
is executing, it only has access to the data structures that represent the source code of some-conditional-expr
and then-expr
, not what they will evaluate to, and not what any of their subexpressions will evaluate to.
Writing macros (meta logic) without keeping straight the distinction between logic(clojure without macros) and meta logic is a usually a bad idea. There are kind of recipes you can follow that will usually work without having to bother with that distinction, but they only work for simpler macros.
But simpler macros are the best anyway
Every macro has those same restrictions. If you think of them as source-code-to-source-code translations, the only inaccurracy in that is it isn't the text of the source code, but the data structures that you get from reading the text of the source code, that they manipulate.
The recipe is write a function that does what you want to do, which may need to take arguments as functions so you can control evaluation etc, then write a macro that just translates the syntax you want into a call to that function
So if you wanted to write the with-open macro:
(defn with-open-fn [resource body-fn] (try (body-fn resource) (finally (.close resource))))
Ugh, on my phone
Then the macro is just (defmacro with-open [[n exp] body]
(with-open-fn exp (fn [n] body)))`
Yeah, I get the idea of keeping your macros small, do most of your stuff in normal functions, and macros are for code-to-code transformations. It just surprised me that it's not possible when you have a macro you can't change that relies on the form passed to it (even if it is a bad idea).
You can change based on the form
Which in this case is a symbol
But you want what the form evaluates to, which is whatever you setup in your local binding
Which you cannot
Anyway, thanks for the help and discussion everyone.
After thinking about your comments some more, I think I understand it better now. To put it in my own words, getting the value of form
requires an evaluation before making the s-expression that starts with the other macro, then another separate evaluation would be required to actually run it. Because two evaluations need to happen, it can only be done by explicitly calling eval
(which should be avoided).
Thanks again for the help @hiredman and @andy.fingerhut.
This is an excellent simplification of what I was struggling with--thanks!
Another way to think about it, for the expression (fn [x] (m x))
if m is a macro, mechanical, how can it ever know what x is bound to
And (let [x ...] (m x))
is more or less ((fn [x] (m x)) ...)
, It isn't literally the same thing in clojure for reasons that have to do with how it compiles to jvm byte code, but in there are lisps where it is
What’s the best place for a simple emacs editing question regarding paredit in clojure?
#emacs