select-keys with default value - clojure

Is there a function like select-keys but with default values for missing keys?
The reason I'm asking is that I'm using korma to query a database. I found a bug where using select-keys is not intuitive when it has no matches.
Example:
(delete t
(where
(select-keys {:k1 "v1" :k2 "v2"} [:k1])))
Same as:
(delete t
(where {:k1 "v1"}))
Which translates through korma to something like:
delete from t where k1='v1'
Nice. select-keys creates just the query map I want.
But:
(delete t
(where
(select-keys {:k2 "v2"} [:k1])))
Same as:
(delete t
(where {}))
Which translates through korma to something like:
delete from t
Which will delete my entire table t :(
I thought about using (merge defaults (select-keys ...)) or create a function select-keys-with-default, but I felt that like (get m :blah "default") there should probably be something built-in that I'm missing.
Browsing through the docs, or related functions offered there, didn't show anything useful out-of-the-box.

You probably don't want to execute a query at all when the selection is empty. So something like this would probably do:
(when-let [selection (not-empty (select-keys ...))]
(delete t (where selection)))

Related

Clojure ring-cors/wrap-cors setup

I'm using ring-cors and trying to pass a cors-policy for the (wrap-cors) function. This is what my code looks like:
(def cors-policy
{:access-control-allow-origin [#"http://localhost:8080"]
:access-control-allow-methods [:get :put :post]})
(def dev-handler (-> #'japi/routes
wrap-reload
wrap-params
(wrap-cors cors-policy) ;; <- Here
wrap-json-response
(wrap-defaults api-defaults)
push-state/handle))
This results in an error:
No value supplied for key: {:access-control-allow-origin
#{"http://localhost:8080"}, :access-control-allow-methods #{:get :post :put}}
Looking at the source code for (wrap-cors) it looks like the error is coming from trying to apply (hash-map) to my cors-policy map. It seems like I cannot pass a map definition but instead I have to pass the keys/values explicitly when calling (wrap-cors). Any ideas to work around this?
I've tried (apply hash-map cors-policy) in the repl and that works fine, however when passing a dummy handler such as (wrap-cors identity cors-policy) this again results in the same error.
Edit: cfrick's answer is correct, note however that I had to remove shadow-cljs' (push-state/handle) handler at the end of my dev-handler definition for my setup to work.
The wrapper uses a "pattern" that is sometimes seen and focuses on
"human consumption" of the function. It takes the "rest" of the
arguments and turns the pairs of it into a map. This is already "meh"
for humans and is utterly bad for machines (e.g. to pass as arguments).
You have to do the call it like this:
(wrap-cors $handler :a 1 :b 2)
So the easiest way from here would be:
(def cors-policy
[:a 1
:b 2])
(apply wrap-cors $handler cors-policy)
Or if you want to stick with the map (IMHO a good approach), you have to
flatten the map beforehand. e.g.
(apply wrap-cors $handler (into [] cat cors-policy))
But with the use of the threading macro -> this becomes harder to do
now (-> is just a macro and the resulting code would be (apply $handler wrap-cors ...) which is unintended.
So at this point I'd add my own defn that just takes the handler
again. E.g. something like
(defn cors-wrapper
[handler config-map]
(apply wrap-cors handler (into [] cat config-map)))

assoc with argument clojure

If I have an hash-map and I want to assoc a value to it, and I get the key as an argument, what should i do?
(defn define [name type kind] "define new var in one of the tables"
(if (or (= type "static") (= type "field"))
(def classScope (assoc classScope name (list type kind (addCount kind))))
(def methodScope (assoc methodScope name (list type kind (addCount kind))))
)
)
My problem is that i can't use :name, and not 'name.
Thanks!!
Update: If you want your keys to be in keyword form, just call keyword on them....
(defn my-map-fn [name type kind]
(assoc some-map (keyword name) (some-fn type kind)))
e.g.
(my-map-fn "some-name" "some-type" "some-kind") => {:some-name some-val}
Note that you shouldn't use def inside of defn. It looks like you want to keep a map of data and as you call define you want to store some more data in that map. A way that I go about this is to use atoms (there are other ways too).
(defonce classScope (atom {})
(defonce methodScope (atom {}))
(defn define
"if you want a doc string it goes here"
[name type kind]
(swap! (if (#{"static" "field"} type) classScope methodScope)
#(assoc % name (list type kind (addCount kind)))))
the benefit here is you get atomic updates and calls to define that may happen really close together won't butt heads.
Let's start with your explanatory comment:
I'm trying to create an hash-map that's like a symbol table: I will
identify each variable by its name, and it will have a list with its
type, kind and index in the code.
A few thoughts:
Don't use a list for the variable's characteristics; use a map.
You can think of the name of a variable as
a plain old string
a symbol
a keyword
Any of these works as the key of a map entry. Keep it simple. Use a string.
You're going to need such a table for every scope. And a scope should know its enclosing scope.
The descriptors static and field are not types; nor are they
alternatives in - say - Java.
I suggest you look at clojure.spec and typed Clojure to see how similar problems are handled within Clojure.

How to add a record to an existing map?

I have a map obtained from running a sql query.
(defn print-info
[rs]
(doseq [req rs]
(let [rs1 req]
(println "rs1" rs1))))
(defn display-info
[uname]
(sql/with-connection
db
(sql/with-query-results rs
["Select * from user"]
(print-info rs))))
Now i have copied the result set into another map. Is there any way to add another record to this existing map and return this new map to another function ?
If you use the non-deprecated query syntax which was explained in your other question, you can use the :row-fn to manipulate each record in a resultset. A default row is a map, so if you use only functions that return a map (like in this case select-keys), you can just use assoc to add a new key-value pair to the map.
(query db ["select * from user"]
:row-fn #(assoc (select-keys % [:name]) :type :user))
Generally speaking when I want to make a fix to a record I'll use merge, which merges any number of maps in a left-to-right direction. Thus:
user=> (merge {:foo 1 :bar 2 :ban 3} {:bar 20} {:ban 300})
{:foo 1, :bar 20, :ban 300}
Although the assoc approach mentioned above also works, I find merge generally more useful and more flexible.
This applies to anything represented as a map, of course, not just records!
As an aside I'd strongly recommend using [Korma|http://sqlkorma.com/] for your database queries.

Korma: or'ing dynamically generated where clauses

I have a set of dynamically generated parameters in a form of a map like
(def clauses {:apples 23 :plums 0 :bananas 7})
and I want to have it or'ed in a where statement, so it should become an equivalent of the Korma query:
(select fruit-shop
(where (or {:apples 23}
{:plums 0}
{:bananas 7})))
Generating a list of maps is quite easy:
(map #(apply array-map %)
(into [] clauses))
But one can't use (or statement applied to it, because it's handled at macro expansion time, before the clauses becomes bound to its value.
What statement should be used in such case?
After getting familiar with Korma source code, I have found korma.sql.fns/pred-or function that replaces or statements in where and having. So I wrote the following helper function that takes a map argument
(require '[korma.sql.fns :refer [pred-or]])
(defn or*
[m]
(apply pred-or
(map #(apply array-map %)
(into [] m))))
Given that, the intended query will look like this
(select fruit-shop
(where (or* clauses)))

Destructuring forms and Compojure?

I'd thought I'd post this as I got it to work through guesswork without a real understanding of what's going on and I thought it might be helpful if someone explained it.
I understand how to get at an element of the :params map in a Compojure handler:
(GET "/something" [some_arg] "this is the response body")
or
(GET "/something" {{some_arg "some_arg"} :params} "this is the response body")
although I don't completely understand what the {some_arg "some_arg"} part is doing :(
I also wanted to access the :remote-addr part of the request as well as some_arg. And I ended up with
(GET "/something" {{some_arg "some_arg"} :params ip :remote-addr}
(do-something-with some_arg ip))
So, I get that the unquoted strings some_arg and ip are the names of variables to which I want the values bound but the map above isn't a valid Clojure map. How does it work?
I also get that this is evaluated against the Ring request map (which is somehow supplied by the defroutes macro) but the expression above isn't a function or macro definition so how can it 'exist' as a valid expression in my code? Is there some sort of suspension of the normal rules for macro arguments? I've been unable to find a definition of the syntax of destructuring forms comprehensible to this non-Lisp'er.
The map is a valid destructuring map. In any place where you bind names, you can use destructuring. You could do the same thing in a let, like this:
user=> (let [{{some-arg "some_arg"} :params ip :remote-addr} {:remote-addr "127.0.0.1" :params {"some_arg" "some_value"}}] [ip some-arg])
["127.0.0.1" "some_value"]
I wrote a post about map destructuring in the context of named arguments, but it applies here. You might find this useful: Clojure - named arguments
There are a lot of blog posts demonstrating destructuring, including this one. I'm not sure which one would be a canonical place to learn from.
I don't pretend to know what exactly compojure does with that map under the hood, but I presume it throws it in a let or something similar as I demonstrated above. GET is a macro, so it doesn't have to evaluate the map you pass it, which is why you wouldn't get an error unless it evaluated it.
user=> (defmacro blah [m])
#'user/blah
user=> (blah {a "b" c "d"})
nil
user=> (defn blah [m])
#'user/blah
user=> (blah {a "b" c "d"})
java.lang.Exception: Unable to resolve symbol: a in this context (NO_SOURCE_FILE:9)
Under the hood, magic happens to that map and it gets passed to a function called destructuring that does the destructuring magic.
There isn't really anything special going on here other than normal macro/special form foo and delayed evaluation.
Destructing takes place within a binding form, and for map destructuring the var to be bound is on the left, and the key is on the right:
user=> (let [{a :foo} {:foo :bar}]
user=* a)
:bar
Compojure is doing a binding form behind the scenes, so that map destructuring form you were using above is effectively turned into something like:
(let [{{some_arg "some_arg"} :params} request]
...)
Where request is an implicitly provided map.
The vector version (e.g., [some_arg]), is an alternative that just binds against the :params map contained in the request.