I'm setting up config map using environ for fetching up env variables. Since environ normalizes upper case to lowercase and "_" characters to "-", I ended up with repetitions of keywords
(def config {:consumer-key (env :consumer-key)
:keystore-password (env :consumer-key)
:ssl-keystore-password (env :ssl-keystore-password)
:ssl-certificate-name (env :ssl-certificate-name)
:callback-url (env :callback-url)
:mp-private-key (env :mp-private-key)
:merchant-checkout-id (env :merchant-checkout-id)
:is-sandbox (env :is-sandbox)})
is there a way to prevent this repetition? maybe a function which receives a keyword and returns some kind of key value pair for the map?
As mentioned in the comments, since env is a map you can just use select-keys with a list of keys to copy:
(def config
(select-keys env [:consumer-key :is-sandbox
:keystore-password :ssl-keystore-password :ssl-certificate-name
:callback-url :mp-private-key :merchant-checkout-id]))
Alan Thompson's approach is reasonable if you have an arbitrary function rather than specifically a map.
Here is one way to do it by defining a helper function:
(def env {:consumer-key 1
:ssl-key 2
:mp-private-key 3})
(def key-list (keys env))
(defn extract-from
[src-fn keys]
(into (sorted-map)
(for [key keys]
{key (src-fn key)} )))
(println "result:" (extract-from env key-list))
=> result: {:consumer-key 1, :mp-private-key 3, :ssl-key 2}
Note that for testing purposes I used a trick and substituted a map env for the function env from the environ library. This works since a map can act like a function when looking up its keys. It will still work for an actual function like environ.core/env.
Related
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)))
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.
I declared a map in clojure using
(def finalMap {})
I am appending values to it inside a function using assoc but they are not appending, the map is remaining empty. I think it is due to immutability, can I in some way make a global map mutable.The function is a recursive one and I am appending values each time the function is called.
(defn func [arg1 arg2]
;(map append inside let)
(dorun (for [i (range 0 index)]
(do
(func(arg1 arg2))))))
Can you help me with the correct way to do this?
If you want a mutable map then you should create an atom:
(def final-map (atom {}))
Also normally you would use assoc to add more key value pairs to it. However you will need to use swap! just to be able to call assoc:
(swap! final-map assoc :a "a value")
This will add a key/value pair where the key is the keyword :a and the value is the String "a value".
It might be good to view some other examples of using assoc. Realise that in the code above assoc is being called with the old value of final-map as its first argument, and returning the new value of final-map.
I want to get following results when I evaluate edit-url and (edit-url 1).
edit-url --> "/articles/:id/edit"
(edit-url 1) --> "/articles/1/edit"
Is it possible to define such a Var or something?
Now, I use following function, but I don't want to write (edit-url) to get const string.
(defn edit-url
([] "/articles/:id/edit")
([id] (str "/articles/" id "/edit")))
Thanks in advance.
If those behaviors are exactly what you want, print-method and tagged literals may be used to imitate them.
(defrecord Path [path]
clojure.lang.IFn
(invoke [this n]
(clojure.string/replace path ":id" (str n))))
(defmethod print-method Path [o ^java.io.Writer w]
(.write w (str "#path\"" (:path o) "\"")))
(set! *data-readers* (assoc *data-readers* 'path ->Path))
(comment
user=> (def p #path"/articles/:id/edit")
#'user/p
user=> p
#path"/articles/:id/edit"
user=> (p 1)
"/articles/1/edit"
user=>
)
edit-url will either have the value of an immutable string or function. Not both.
The problem will fade when you write a function with better abstraction that takes a string and a map of keywords to replace with words. It should work like this
(generate-url "/articles/:id/edit" {:id 1})
Clojure is a "Lisp 1" which means that is has a single namespace for all symbols, including both data scalars and functions. What you have written shows the functionally of both a string and a function but for a single name, which you can do in Common Lisp but not Clojure (not that a "Lisp 2" has its own inconveniences as well).
In general this type of "problem" is a non issue if you organize your vars better. Why not just make edit-url a function with variable arity? Without arguments it returns something, with arguments it returns something else. Really the possibilities are endless, even more so when you consider making a macro instead of a function (not that I'm advocating that).
Given a list of names for variables, I want to set those variables to an expression.
I tried this:
(doall (for [x ["a" "b" "c"]] (def (symbol x) 666)))
...but this yields the error
java.lang.Exception: First argument to def must be a Symbol
Can anyone show me the right way to accomplish this, please?
Clojure's "intern" function is for this purpose:
(doseq [x ["a" "b" "c"]]
(intern *ns* (symbol x) 666))
(doall (for [x ["a" "b" "c"]] (eval `(def ~(symbol x) 666))))
In response to your comment:
There are no macros involved here. eval is a function that takes a list and returns the result of executing that list as code. ` and ~ are shortcuts to create a partially-quoted list.
` means the contents of the following lists shall be quoted unless preceded by a ~
~ the following list is a function call that shall be executed, not quoted.
So ``(def ~(symbol x) 666)is the list containing the symboldef, followed by the result of executingsymbol xfollowed by the number of the beast. I could as well have written(eval (list 'def (symbol x) 666))` to achieve the same effect.
Updated to take Stuart Sierra's comment (mentioning clojure.core/intern) into account.
Using eval here is fine, but it may be interesting to know that it is not necessary, regardless of whether the Vars are known to exist already. In fact, if they are known to exist, then I think the alter-var-root solution below is cleaner; if they might not exist, then I wouldn't insist on my alternative proposition being much cleaner, but it seems to make for the shortest code (if we disregard the overhead of three lines for a function definition), so I'll just post it for your consideration.
If the Var is known to exist:
(alter-var-root (resolve (symbol "foo")) (constantly new-value))
So you could do
(dorun
(map #(-> %1 symbol resolve (alter-var-root %2))
["x" "y" "z"]
[value-for-x value-for-y value-for z]))
(If the same value was to be used for all Vars, you could use (repeat value) for the final argument to map or just put it in the anonymous function.)
If the Vars might need to be created, then you can actually write a function to do this (once again, I wouldn't necessarily claim this to be cleaner than eval, but anyway -- just for the interest of it):
(defn create-var
;; I used clojure.lang.Var/intern in the original answer,
;; but as Stuart Sierra has pointed out in a comment,
;; a Clojure built-in is available to accomplish the same
;; thing
([sym] (intern *ns* sym))
([sym val] (intern *ns* sym val)))
Note that if a Var turns out to have already been interned with the given name in the given namespace, then this changes nothing in the single argument case or just resets the Var to the given new value in the two argument case. With this, you can solve the original problem like so:
(dorun (map #(create-var (symbol %) 666) ["x" "y" "z"]))
Some additional examples:
user> (create-var 'bar (fn [_] :bar))
#'user/bar
user> (bar :foo)
:bar
user> (create-var 'baz)
#'user/baz
user> baz
; Evaluation aborted. ; java.lang.IllegalStateException:
; Var user/baz is unbound.
; It does exist, though!
;; if you really wanted to do things like this, you'd
;; actually use the clojure.contrib.with-ns/with-ns macro
user> (binding [*ns* (the-ns 'quux)]
(create-var 'foobar 5))
#'quux/foobar
user> quux/foobar
5
Evaluation rules for normal function calls are to evaluate all the items of the list, and call the first item in the list as a function with the rest of the items in the list as parameters.
But you can't make any assumptions about the evaluation rules for special forms or macros. A special form or the code produced by a macro call could evaluate all the arguments, or never evaluate them, or evaluate them multiple times, or evaluate some arguments and not others. def is a special form, and it doesn't evaluate its first argument. If it did, it couldn't work. Evaluating the foo in (def foo 123) would result in a "no such var 'foo'" error most of the time (if foo was already defined, you probably wouldn't be defining it yourself).
I'm not sure what you're using this for, but it doesn't seem very idiomatic. Using def anywhere but at the toplevel of your program usually means you're doing something wrong.
(Note: doall + for = doseq.)