I am trying to use transit-cljs in order to consume some JSON service and turn it into a cljs data-structure.
In my cljs code I do:
(def r (transit/reader :json))
(def json (generate-string {:foo "bar" :baz 5}))
(def data (transit/read r json))
I am no be able to access this like a map now? something like:
("foo" data)
Thanks in advance
Trying this in ClojureScript :
(def r (transit/reader :json))
(def json "{\"foo\":\"bar\", \"baz\" : 123}")
(def data (transit/read r json))
(println "JSON " json)
(println "DATA " data)
Yields the following in my browser :
JSON {"foo":"bar", "baz" : 123}
DATA {foo bar, baz 123}
I am not sure where your function generate-string comes from though.
You should have noted the following error :
("foo" data) ;; Uncaught TypeError: "foo".call is not a function
You can do either :
(data "foo")
(get data "foo")
I personally like the second more when there is a string and no keyword.
Related
I have a database with a status entity that I'd like to be able to fetch in many different ways. As a result, I'd like to build the WHEN clause of my query based on the content of a map.
For instance, like this:
(get-status *db* {:message_id 2 :user_id 1 :status "sent"})
;; or
(get-status *db* {:message_id 2})
;; or
(get-status *db* {:user_id 1})
;; etc.
I'm struggling using hubsql's clojure expressions. I am doing the following:
-- :name get-status
-- :doc fetch the status of a specific message
-- :command :query
-- :return :many
/* :require [clojure.string :as s] */
SELECT
*
FROM
message_status
/*~
(let [params (filter (comp some? val) params)]
(when (not (empty? params))
(str "WHERE "
(s/join ","
(for [[field value] params]
(str field " = " (keyword field)))))))
~*/
However, here is how the request is prepared by hugsql:
=> (get-status-sqlvec {:message_id 2 :user_id 1})
["SELECT\n *\nFROM\n message_status\nWHERE ? = ?,? = ?" 2 2 1 1]
Whereas I want something like:
=> (get-status-sqlvec {:message_id 2 :user_id 1})
["SELECT\n *\nFROM\n message_status\nWHERE message_id = 2, user_id = 1"]
I finally managed to get this working. The above code had two issues.
First, we have
(s/join ","
(for [[field value] params]
(str field " = " (keyword field)))
Since field is a keyword, this actually generates this kind of string: :foo = :foo, :bar = :bar. The keywords are then replaced by ? by hugsql. What we want instead is build this kind of string foo = :foo, bar = :bar, which we can do with this code:
(s/join ", "
(for [[field _] params]
(str (name field) " = " field))))))
The second problem is that the WHEN clause is not even valid SQL. The above code ends up generating requests such as:
SELECT * FROM message_status WHERE foo = 2, bar = 1
The commas in the WHERE clause should be AND, so the final (working) code is:
-- :name get-status
-- :doc fetch the status of a specific message
-- :command :query
-- :return :many
/* :require [clojure.string :as s] */
SELECT
*
FROM
message_status
/*~
(let [params (filter (comp some? val) params)]
(when (not (empty? params))
(str "WHERE "
(s/join " AND "
(for [[field _] params]
(str (name field) " = " field))))))
~*/
(def val { :type "bar" })
(-> val
(case ???
"bar" "bar type"
"baz" "baz type"
"other type"))
I'd like to include a case in a threading macro so I can branch based on one of the keys of val, a hash map. Is this possible?
EDIT: I need to thread val not a key from val as further functions will need the whole of val. I essentially want to branch to a function within the threading macro based on a key of val. But still pass val onward.
Consider using a multimethod dispatch here:
(defmulti some-operation :type)
(defmethod some-operation "bar"
[val]
(println "Bar type!")
(assoc val :x 42))
(defmethod some-operation "baz"
[val]
(println "Baz type!")
(assoc val :x 100))
(-> {:type "bar"}
some-operation
some-other-operation)
This is something which seems to work and serves as a better example of what I want to do:
(def val { :type "bar" })
(-> val
(do-something-to-val-based-on-type)
(do-something-else-to-val))
(defn do-something-to-val-based-on-type [val]
(let [:type (:type val)]
(case type
"bar" (do-something-to-bar-type-val val)
"baz" (do-something-to-baz-type-val val)
val))) ;; default, no-op
(defn do-something-to-bar-type-val [val]
;; something
val)
(defn do-something-to-baz-type-val [val]
;; something
val)
also, since a threading macro simply adds an item to a seq for every "action", you can easily use anonymous function for that:
user> (def val { :type "bar" })
#'user/val
user> (-> val
((fn [{type :type}]
(case type
"bar" "bar type"
"baz" "baz type"
"other type"))))
;;=> "bar type"
if you wish, you can also make up special macro, rearranging let for usage in ->:
user> (defmacro let-inv [x binding & body]
`(let [~binding ~x] ~#body))
#'user/let-inv
user> (-> val
(let-inv {type :type}
(case type
"bar" "bar type"
"baz" "baz type"
"other type")))
;;=> "bar type"
This is easily accomplished using the it-> threading macro from the Tupelo library:
(ns tst.clj.core
(:use clj.core tupelo.test)
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(def val { :type "bar" })
(println "result => "
(it-> val
(case (grab :type it)
"bar" "bar-type"
"baz" "baz-type"
"other-type")))
to yield the desired result:
result => bar-type
The it-> macro assigns the intermediate value to the symbol it at each stage of the pipeline. In this case we use the grab function to extract the :type value. It works like (:type it) but will throw if the key is not found.
Another example:
(it-> 1
(inc it) ; thread-first or thread-last
(+ it 3) ; thread-first
(/ 10 it) ; thread-last
(str "We need to order " it " items." ) ; middle of 3 arguments
;=> "We need to order 2 items." )
I wrote a short function for debugging:
(defn printvar
"Print information about given variables in `name : value` pairs"
[& vars]
(dorun (map #(println (name %) ":" (eval %)) vars)))
Then I tried to test it:
(defn -main [arg1 arg2]
(def moustache true) (def answer 42) (def ocelots-are "awesome!")
(printvar 'moustache 'answer 'ocelots-are)
(printvar 'arg1 'arg2))
But ran into some really confusing behaviour:
$ lein repl
> (-main "one" "two")
# moustache : true
# answer : 42
# ocelots-are : awesome!
# CompilerException java.lang.RuntimeException: Unable to resolve symbol: arg1 in this context, compiling:(/tmp/form-init4449285856851838419.clj:1:1)
$ lein run "one" "two"
# Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: moustache in this context, compiling:(/tmp/form-init4557344131005109247.clj:1:113)
Experimenting a bit more, I discovered this:
(defn -main [arg1 arg2]
(meta #'arg1))
# Exception in thread "main" java.lang.RuntimeException: Unable to resolve var: arg1 in this context, compiling:(dict_compress/core.clj:9:11)
(defn -main [arg1 arg2]
(def arg1 arg1)
(meta #'arg1))
# {:ns #<Namespace dict-compress.core>, :name arg1, :file dict_compress/core.clj, :column 2, :line 10}
Now I'm totally confused.
What exactly are you passing when you do (f 'var) and (f var)?
Why are there different behaviours when run from the REPL versus directly?
What's the difference between a received argument versus a defined variable?
How can I fix my code?
Am I going about debugging the wrong way?
Inside printvar the def'ed vars moustache answer and ocelots-are are correctly printed because def defines them as "globals".
Meaning there is a moustache var that the printvar function can "see".
Think about it this way, this works:
(def moustache 43)
(defn printvar []
(println moustache)
(defn main [arg1]
(printvar))
This doesn't work:
(defn printvar []
(println arg1))
(defn main [arg1]
(printvar))
Which is exactly what you're doing, passing the parameter name to eval does nothing for the parameter scope (printvar won't be able to see it).
A couple of issues with your code:
You shouldn't be defing inside a function, local bindings are defined with let
If you want to eval you need to consider scope of what you're evaling.
Just to elaborate on #Guillermo's comment, here is a macro that does the printing of any variable, locally or globally bound.
(defmacro printvar
([])
([v & more]
`(let [v# ~v]
(println '~v "=" v#)
(when (seq '~more)
(printvar ~#more)))))
With this you can try the sequence :
user> (def glob-var "foo")
#'user/glob-var
user> (defn -main [loc1 loc2]
(printvar glob-var loc1 loc2))
#'user/-main
user> (-main "bar" 42)
glob-var = foo
loc1 = bar
loc2 = 42
nil
user>
I'm new to Clojure, and I am using ring.velocity to develop a webapp.
Here is my ring.velocity.core/render method:
(defn render
[tname & kvs]
"Render a template to string with vars:
(render :name \"dennis\" :age 29)
:name and :age are the variables in template. "
(let [kvs (apply hash-map kvs)]
(render-template *velocity-render tname kvs)))
For this simple example, it works fine:
(velocity/render "test.vm" :name "nile")
But sometimes, we can't hard code the key value pairs. A common way:
(defn get-data [] {:key "value"}) ;; define a fn get-data dynamic.
(velocity/render "test.vm" (get-data));; **this go wrong** because in render fn , called (apply hash-map kvs)
Has the error:
No value supplied for key: ....
It looks like it is treated as if it was a single value. I've changed the type to [], {}, and (), but each of these fails.
My question is: What does & kvs in clojure mean? How can I dynamically create it and pass it to method?
ADD A Simple Test
(defn params-test [a & kvls]
(println (apply hash-map kvls)))
(defn get-data []
[:a "A"])
(defn test[]
(params-test (get-data))
Result
No value supplied for key:((:a "A"))
The problem here is that you're trying to create a hash-map from a single list argument instead of list of arguments.
Use
(apply hash-map kvls)
instead of
(hash-map kvls)
In your original question you can try to use apply with partial
(apply (partial velocity/render "test.vm") (get-data))
I'm trying to build an XML structure using the internal data types from BaseX from Clojure.
(defn basex-elem [token-name dict]
(let [elem (org.basex.query.item.FElem.
(org.basex.query.item.QNm. token-name))]
(for [[k v] dict]
(do
(println "THIS IS REACHED")
(let [k-name (org.basex.query.item.QNm. (.getName k))
k-attr (org.basex.query.item.FAttr.
k-name
org.basex.util.Token/token v))]
(.add elem k-attr))))
elem))
When using this to cry to create an element, "THIS IS REACHED" is never printed:
(def test-elem (basex-elem "element-name" {:key1 "value1", :key2 "value2"}))
; => #'user/test-elem
And thus the value comes back without any attributes:
test-elem
; => #<FElem <element-name/>>
But adding attributes works otherwise.
(.add test-elem
(org.basex.query.item.FAttr.
(org.basex.query.item.QNm. "foo")
(org.basex.util.Token/token "bar")))
; => #<FElem <element-name foo="bar"/>>
Thus, presumably I'm doing something wrong with the loop. Any pointers?
for is not a loop construct in clojure, rather it's a list comprehension and produces a lazy sequence.
Use doseq instead when side effects are intended.