Using compojure-api, as in:
(defapi app
(swagger-ui)
(swagger-docs
{:info {:title "Sample api"}})
(GET* "/" []
:no-doc true
(ok "hello world"))
(context* "/api" []
:tags ["thingie"]
(GET* "/plus" []
:return Long
:query-params [x :- Long, {y :- Long 1}]
:summary "x+y with query-parameters. y defaults to 1."
(ok (+ x y)))))
how do I access the ring-session?
based on docs from here: https://github.com/metosin/compojure-api/blob/master/src/compojure/api/core.clj , defapi is the following macro:
(defmacro defapi
[name & body]
`(def ~name
(api ~#body)))
as you can see it just defines var with the result of api macro call (and api creates a ring handler)
so you could use it without defapi, and wrap ring session:
(def app
(-> (api (swagger-ui)
(swagger-docs
{:info {:title "Sample api"}})
(GET* "/" []
:no-doc true
(ok "hello world"))
(context* "/api" []
:tags ["thingie"]
(GET* "/plus" []
:return Long
:query-params [x :- Long, {y :- Long 1}]
:summary "x+y with query-parameters. y defaults to 1."
(ok (+ x y))
ring.middleware.session/wrap-session))
I guess after that you should be able to use the session normally, as described in https://github.com/ring-clojure/ring/wiki/Sessions . Haven't tester it, but i think it's the right way
Related
This macro returns the values of the "magic" &env as a map, so that
(let [xyz "ZYX"] (get-env)) returns {xyz "ZYX"}, where the key is a Symbol.
(defmacro get-env []
(let [ks (keys &env)]
`(zipmap '~ks [~#ks])))
The expression '~ks evaluates the ks into Symbols at the macro-expansion phase (right?), but then quotes the expansion, so that the Symbols don't get evaluated into their values ("ZYX" in our example), but rather stay as Symbols (xyz). Is that right?
About [~#ks]: It evaluates ks into an seq of Symbols at the macro-expansion phase (right?) (and splices them and forms a vector with []). But how does that allow these Symbols to get further evaluated into their values ("ZYX" in our example) -- is there a second evaluation step, applied immediately after the first?
Another variant is
(defmacro local-env [] (->> (keys &env)
(map (fn [k] [(list 'quote k) k])) (into {})))
Your macro takes all the keys from the env. Then it uses the keys (a
list of symbols) to zip both the list of keys with spliced symbols
inside a vector. So what you get from
(let [x 42]
(get-env))
is
(let [x 42]
(zipmap '(x) [x]))
This is a compile-time transformation of your code (the whole point of
macros). The resulting code at runtime will use the 42 from the bound
x.
Preface
You may also be interested in the book "Clojure Macros", and in this StackOverflow question:
How do I write a Clojure threading macro?
Discussion
When in doubt, ask the compiler. Consider this code using my favorite template project:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defmacro getenv []
(prn :env &env)
(prn :env-meta (meta &env))
(prn :form &form)
(prn :form-meta (meta &form)))
(defn go []
(newline)
(prn :01)
(getenv)
(let [x 1
y "yyy"]
(newline)
(prn :02)
(getenv))
)
(dotest
(go))
with output
:env nil
:env-meta nil
:form (getenv)
:form-meta {:line 15, :column 3}
:env {x #object[clojure.lang.Compiler$LocalBinding 0x1ab07559 "clojure.lang.Compiler$LocalBinding#1ab07559"], y #object[clojure.lang.Compiler$LocalBinding 0x26c79134 "clojure.lang.Compiler$LocalBinding#26c79134"]}
:env-meta nil
:form (getenv)
:form-meta {:line 21, :column 5}
Testing tst.demo.core
:01
:02
so we can see the 4 (prn ...) outputs for each call to getenv. In the case where there are no local bindings, we get
&env ;=> nil
and for the case with the let we get a map like
(let [env-val (quote
{x :compiler-local-1
y :compiler-local-1})
ks (keys env-val)
ks-vec [ks]
]
(spyx env-val)
(spyx ks)
(spyx ks-vec)
)
with result
env-val => {x :compiler-local-1, y :compiler-local-1}
ks => (x y)
ks-vec => [(x y)]
At this point, I'm not quite sure what your desired result is. Could you modify the question to add that?
BTW, there is no hidden 2nd step, if I understand your question correctly.
Also
I rewrite your local-env and got the following result:
(defmacro local-env []
(prn :into-result
(into {}
(mapv
(fn [k] [(list 'quote k) k])
(keys &env)))))
(let [x 1
y "yyy"]
(newline)
(prn :03)
(local-env))
with result
:into-result {(quote x) x,
(quote y) y}
so I think there is some confusion here.
I would like to be able to mock MyFunction however I need the mock to return different values when MyFunction is called.
Is it possible to use with-redefs to return different values based on the call order of a function?
(testing "POST /foo/bar and return ok"
(with-redefs [->Baz (fn [_]
(reify MyProtocol (MyFunction [_] [{:something 1}]))
(reify MyProtocol (MyFunction [_] [{:something 2}])))]
(let [response (routes/foo {:request-method :post
:uri "/foo/bar"
:query-params {}
})]
(is (= (:status response) 200)))))
You could use a mutable collection of the return values, then return/remove values from it on each call.
(defn foo [x] (inc x)) ;; example fn to be mocked
If you wanted to mock three calls to foo returning 1, 2, and 3 respectively:
(with-redefs [foo (let [results (atom [1 2 3])]
(fn [_] (ffirst (swap-vals! results rest))))]
(prn (foo 0))
(prn (foo 0))
(prn (foo 0))
;; additional calls would return nil
(prn (foo 0)))
;; 1
;; 2
;; 3
;; nil
That uses swap-vals! to get the old/new values of the atom, but requires Clojure 1.9 or greater.
If you don't have swap-vals! you could do it (less atomically) like this:
(with-redefs [foo (let [results (atom [1 2 3])]
(fn [_]
(let [result (first #results)]
(swap! results rest)
result)))]
...)
We use Picomock for this, and to assert on the parameters for each call, and to assert on the number of calls. Recommended!
I'm looking to customize how a react element is displayed, this is the code:
(require ["react" :as react])
(defn react-element? [obj]
(if-let [sym (aget obj "$$typeof")]
(= js/Symbol
(type sym))))
(extend-protocol IPrintWithWriter
object
(-pr-writer [obj writer _]
(js/console.log "hello")
(cond (react-element? obj)
(write-all writer
(-> (js->clj obj)
(dissoc "$$typeof")
str))
:else
(write-all writer (str obj)))))
(react/createElement "h2" nil "Hello, World!")
I'm expecting the output to be
{:type "h2", :key nil, :ref nil, :props {:children "Hello, World!"}}}
but it still prints out the normal #js element:
#js {"$$typeof" Symbol(react.element),
:type "h2", :key nil, :ref nil,
:props #js {:children "Hello, World!"}, :_owner nil}
Is there a way to customize this behaviour?
The root cause is that in the standard lib printing code, implements? is used and satisfies? is not used. This effectively limits printing overrides to things that are not native types. (implements? doesn't doesn't work on native types).
This may not be intentional and may be fixed with https://dev.clojure.org/jira/browse/CLJS-2812
Consider this pseudo code:
(defrc name
"string"
[a :A]
[:div a])
Where defrc would be a macro, that would expand to the following
(let [a (rum/react (atom :A))]
(rum/defc name < rum/reactive []
[:div a]))
Where rum/defc is itself a macro. I came up with the code below:
(defmacro defrc
[name subj bindings & body]
(let [map-bindings# (apply array-map bindings)
keys# (keys map-bindings#)
vals# (vals map-bindings#)
atomised-vals# (atom-map vals#)]
`(let ~(vec (interleave keys# (map (fn [v] (list 'rum/react v)) (vals atomised-vals#))))
(rum/defc ~name < rum/reactive [] ~#body))))
Which almost works:
(macroexpand-all '(defrc aname
#_=> "string"
#_=> [a :A]
#_=> [:div a]))
(let* [a (rum/react #object[clojure.lang.Atom 0x727ed2e6 {:status :ready, :val nil}])] (rum/defc aname clojure.core/< rum/reactive [] [:div a]))
However when used it results in a syntax error:
ERROR: Syntax error at (clojure.core/< rum.core/reactive [] [:div a])
Is this because the inner macro is not being expanded?
Turns out the macro was working correctly but the problem occurred because < was inside the syntax quote it got expanded to clojure.core/<, and Rum simply looks for a quoted <, relevant snippet from Rum's source:
...(cond
(and (empty? res) (symbol? x))
(recur {:name x} next nil)
(fn-body? xs) (assoc res :bodies (list xs))
(every? fn-body? xs) (assoc res :bodies xs)
(string? x) (recur (assoc res :doc x) next nil)
(= '< x) (recur res next :mixins)
(= mode :mixins)
(recur (update-in res [:mixins] (fnil conj []) x) next :mixins)
:else
(throw (IllegalArgumentException. (str "Syntax error at " xs))))...
I'm trying to validate the protocol of an instance of defrecord that I generate dynamically using clojure.core/extend
Below You can see that satisfies returns true and (s/validate (s/protocol ...)) doesn't throw exception, but if I run s/with-fn-validation I get a "(not (satisfies? protocol ..... " schema exception although inside this body I keep getting the same true result for (satisfies? protocol x)
(ns wrapper.issue
(:require [schema.core :as s]))
(defprotocol Welcome
(greetings [e] )
(say_bye [e a b])
)
(s/defn greetings+ :- s/Str
[component :- (s/protocol Welcome)]
(greetings component))
(defrecord Example []
Welcome
(greetings [this] "my example greeting!")
(say_bye [this a b] (str "saying good bye from " a " to " b))
)
(defrecord MoreSimpleWrapper [e])
(extend MoreSimpleWrapper
Welcome
{:greetings (fn [this]
(str "wrapping!! " (greetings (:e this)))
)
:say_bye (fn [this a b]
(str "good bye !"))})
(println (satisfies? Welcome (MoreSimpleWrapper. (Example.))))
;;=>true
(println (s/validate (s/protocol Welcome) (MoreSimpleWrapper. (Example.))))
;;=>#wrapper.issue.MoreSimpleWrapper{:e #wrapper.issue.Example{}}
(s/with-fn-validation
(println (satisfies? Welcome (MoreSimpleWrapper. (Example.))))
;;=>true
(println (s/validate (s/protocol Welcome) (MoreSimpleWrapper. (Example.))))
;;=>#wrapper.issue.MoreSimpleWrapper{:e #wrapper.issue.Example{}}
)
(s/with-fn-validation
(greetings+ (MoreSimpleWrapper. (Example.))))
;;=>CompilerException clojure.lang.ExceptionInfo: Input to greetings+ does not match schema: [(named (not (satisfies? Welcome a-wrapper.issue.MoreSimpleWrapper)) component)] {:schema [#schema.core.One{:schema (protocol Welcome), :optional? false, :name component}], :value [#wrapper.issue.MoreSimpleWrapper{:e #wrapper.issue.Example{}}], :error [(named (not (satisfies? Welcome a-wrapper.issue.MoreSimpleWrapper)) component)]}, compiling:(/Users/tangrammer/git/olney/wrapper/src/wrapper/issue.clj:39:69)
Here also a gist with the code
It was a schema bug :)
now fixed in snapshot-0.3.2
https://github.com/Prismatic/schema/issues/164