I'm specifically trying to generate the boilerplate for crud functions to work with the Google App Engine datastore using appengine-magic in Clojure. I'm having difficulty working out how to generate values from a model that I've reproduced below.
(def *model* {:users [{:name "Adam"
:email "adam#gmail.com"
:registered-on "07-05-2011"}
{:name "Greg"
:email "gregory#gmail.com"
:registered-on "11-05-2011"}]
:post [{:title "A"
:authour "Adam"}
{:title "B"
:author "Greg"}]})
I'm fairly new to appengine-magic, but it provides a defentity which allows you to define entities that you can put into the datastore and save! which allows you to save predefined entities into the datastore.
These take the form of:
(ds/defentity Post [title author])
(ds/save! (Post. title author))
Now just to start with I've defined:
(defn list-entities [model]
"Takes a representation of the model and lists the entities in preparation for generating defentities"
(interleave (vec (map first (partition 1 (map (comp symbol capitalize #(str % ".") name) (keys model)))))
(map vec (map keys (map first (vals model))))))
Calling it with:
(list-entities *model*)
Outputs:
(Users. [:name :email :registered-on] Post. [:title :author])
Now I am having difficulty defining gen-entities which will take the output above and repeatedly call ds/defentities defining as many entities as my model requires.
(defmacro gen-entities [entity fields]
`(ds/defentity 'entity 'fields))
Additionally I am in no way certain that this is a reasonable way to go about solving this problem. I'm still very new to macros and probably making several mistakes. Any help/clarity would be appreciated.
NOTE:
That model I've realised is badly designed, the one below is a lot better:
(def *model* {:users [:name :email :registered-on]
:post [:title :author]})
However it is more complex in terms of writing a macro so I will leave it as is.
I think a macro is required, because defentity seems to define a type.
(defmacro gen-entities
[model]
`(do
~#(for [[entity-kw values] model]
(let [entity-sym (-> entity-kw name capitalize symbol)
fields (map (comp symbol name) (keys (first values)))]
`(ds/defentity ~entity-sym [~#fields])))))
You don't have to fiddle the keys and values from each other, just to put them together again with interleave. Mapping over a map will give you the key and the corresponding value in one go.
user=> (macroexpand-1 `(gen-entities ~model))
(do
(ds/defentity Users [name registered-on email])
(ds/defentity Post [title authour]))
Note: this won't work with the model stored in a Var. You'll have to specify the model in the call to gen-entities.
user=> (macroexpand-1 '(gen-entities model))
(
#<IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol>
Related
Well, the title is a mouthful, so I will expand on it. I have the following code (it is incomplete, mostly just for illustration):
(use '[clojure.zip :only [up down right node])
(defn in-zip? [form]
(contains? (-> 'clojure.zip ns-publics vals set) (first form)))
(defn do-something-to-zip-form [fx loc rest]
;; this is where I would do the transform, but for now, I will just
;; return the actual form
form)
(defn transform-zip [form]
(if (in-zip? form)
(do-something-to-zip-form form)
form))
(defmacro gozip [body]
(clojure.walk/postwalk transform-zip body))
The purpose of in-zip? is to take a form and determine whether the evaluated form calls to a function in clojure.zip . So, something like (is-zip? '(clojure.zip/down loc) or (is-zip? '(up loc)) should return true, any form that isn't calling a function within clojure.zip should return false.
I want to be able to call gozip with a form, and have every call to a function in clojure.zip be replaced by my do-something-to-zip-form transformation. Some examples:
(gozip (-> loc down right right (clojure.zip/update 3))
In the above expression, I would like it to run the transform in 5 places (loc,down,right,right, and update) because those are all functions within clojure.zip.
(gozip (let [d (down loc)] (node loc)))
In the above expression, I would like to run transform in 2 places (down, node).
Sorry about being so pedantic about explaining what I am interested in, it is just that I am having trouble explaining exactly what I want, seems easier through examples. I am looking to use gozip in clojure and clojurescript code.
guys,
look at the code first:
(defmacro map-remove- [v w]
`(dosync
(ref-set ~v (dissoc #~v (keyword ~w)))))
(defmacro set-remove- [v w]
`(dosync
(ref-set ~v (disj #~v ~w))))
(defmacro clean- [v]
`(dosync
(ref-set ~v (empty #~v))))
They work fine now , but I want write a more general macro to combine "map-remove-" and "set-remove-" in one. according my C/Java experience I chose "case" but obviously the case can't use in macro defination cause "The test-constants are not evaluated. They must be compile-time literals", the following code won't work:
(defmacro [x]
(case (type x) ;;;;;;;;;;; This will never work!directly reach to default clause
....))
anybody has any suggestion? very appreciate.
You can use the functions map? and set? to test whether a value is a map or a set respectively.
I'm not sure I have enough perspective on what you're trying to do here though - I've substituted a macro instead of a function, because I can't see the necessity for a macro in the sample you've given.
;; Create a function to remove in a different way when given a map/set and a string
(defn remove- [v w]
(dosync
(cond
(map? #v)
(ref-set v (dissoc #v (keyword w)))
(set? #v)
(ref-set v (disj #v w)))))
;; Set up some mutable refs
(def m (ref {:a 1 :b 2}))
(def s (ref #{"a" "b" "c"}))
;; Remove from the refs
(remove- m "b") => {:a 1}
(remove- s "b") => #{"a" "c"}
On a side note - are you sure you need to use refs? I know that coming from a C/Java background, mutability is the default, but I've never actually had to use it in Clojure so far. Clojure places a lot of emphasis on immutability, and most things can be done (often very elegantly) just by using functions on immutable values.
The macro, transform!, as defined below seems to work for => (transform! ["foo" 1 2 3]). The purpose is to take in a list, with the first element being a string that represents a function in the namespace. Then wrapping everything into swap!.
The problem is that transform! doesn't work for => (transform! coll), where (def coll ["foo" 1 2 3]). I am getting this mystery exception:
#<UnsupportedOperationException java.lang.UnsupportedOperationException: nth not supported on this type: Symbol>
The function:
(defmacro transform!
" Takes string input and update data with corresponding command function.
"
[[f & args]] ;; note double brackets
`(swap! *image* ~(ns-resolve *ns* (symbol f)) ~#args))
I find it strange that it works for one case and not the other.
Macros work at compile-time and operate on code, not on runtime data. In the case of (transform! coll), the macro is being passed a single, unevaluated argument: the symbol coll.
You don't actually need a macro; a regular function will suffice:
(defn transform! [[f & args]]
(apply swap! *image* (resolve (symbol f)) args)))
Resolving vars at runtime could be considered a code smell, so think about whether you really need to do it.
You're passing a symbol to the macro, namely coll. It will try to pull that symbol apart according to the destructuring statement [f & args], which won't be possible of course.
You can also use (resolve symbol) instead of (ns-resolve *ns* symbol).
I am currently developing a small CMS using the wonderful Enlive as templating engine. Enlive has a macro called at that takes a node (a map) specifying the HTML snippet and an arbitrary number of tuples each consisting of a selector (a vector) and a transformation (a closure).
(at a-node
[:a :selector] a-transformation
[:another :selector] another-transformation
...)
Now I want to generate the tuples depending upon incoming data/context. I have tried a lot of different things without success. For example
(let [this (repository/u "http://example.com/ACMECorp")
statements (repository/find-by-subject this)
context {:depth 1}]
`(at (snippet-for 'this 'context)
[root] (set-attr :about (str 'this))
~#(loop [rules []
st statements]
(if-not (seq st)
rules
(recur (conj rules
`[:> (attr= :property ~(str (repository/predicate (first st))))]
`(content (renderit ~(repository/object (first st)) 'context)))
(rest st))))))
Any help is highly appreciated.
-Jochen
Clojure is a Lisp, so you can always fallback to building the code you'd want as a list, and call eval on it. I'm not 100% sure about the code you gave, but I'd guess you just want to enclose your whole syntax-quote in an eval call.
(let [this (repository/u "http://example.com/ACMECorp")
statements (repository/find-by-subject this)
context {:depth 1}]
(eval `(at (snippet-for 'this 'context)
[root] (set-attr :about (str 'this))
~#(loop [rules []
st statements]
(if-not (seq st)
rules
(recur (conj rules
`[:> (attr= :property ~(str (repository/predicate (first st))))]
`(content (renderit ~(repository/object (first st)) 'context)))
(rest st)))))))
Not sure if they are interchangeable, but take a look at the at* function. Seems to me that your problem is at being a macro.
EDIT: They're not. Call it like this:
(at* a-node
[[:a :selector] a-transformation
[:another :selector] another-transformation
...])
I tried to query data from database with jdbc. The problem is some column is array type.
;get that particular column
(def jdbc-array (with-connection *db*
(with-query-results rs ["select * from refgene limit 5"]
(:exonstarts (first rs)))))
;Check if it has a value
(print jdbc-array)
;#<Jdbc4Array {67075873,67078739,67085754,67100417,67109640,67113051,67129424,67131499,67143471,67162932}>nil
;check class
(class jdbc-array)
;org.postgresql.jdbc4.Jdbc4Array
How to convert this array to seq/vector in clojure ? I tried (seq jdbc-array) and (seq (.getArray jdbc-array) but both doesn't work...
If the with-connection option seems clunky to you (it does to me), you can extend the IResultSetReadColumn protocol to convert Jdbc4Array objects into regular or vectors:
Here's one way to do that:
(extend-protocol clojure.java.jdbc/IResultSetReadColumn
org.postgresql.jdbc4.Jdbc4Array
(result-set-read-column [pgobj metadata i]
(vec (.getArray pgobj))))
this will convert all array types into vectors when reading
this approach can also help with the JSON datatype as in this example
Ok, I got it. I need to called getArray before clojure close the connection, or it'll give a nil.
Not sure why... My guess is clojure's laziness.
;work
(with-connection *db*
(with-query-results rs ["select * from refgene limit ?" 5]
(seq (.getArray (:exonends (first rs))))))