I am trying to maintain a queue of orders and "cook" them using the FIFO process. Using the atom I am able to update the queue but the last two line are not working. What I need is to remove the first element and update the atom(queue) with the remaining list.
Thanks,
R.
(ns example.atomqueue
(:use clojure.pprint))
(def orders-queue (atom clojure.lang.PersistentQueue/EMPTY))
(defrecord Order [fplate splate])
(defn add-order [order]
(swap! orders-queue conj order))
(add-order (->Order "Soup" "Fish&Chips"))
(pprint orders-queue)
(add-order (->Order "Salad" "Hamburger"))
(pprint orders-queue)
(add-order (->Order "Rice" "Steak"))
(pprint orders-queue)
;;NOT SURE IF CORRECT
(defn cook-order [orders]
(swap! orders-queue (pop orders)))
;;EXCEPTION
(cook-order orders-queue)
Thanks to Mr. leetwinski this is the right solution:
(ns chaptertwo.atom
(:use clojure.pprint))
(def orders-queue (atom clojure.lang.PersistentQueue/EMPTY))
(defrecord Order [fplate splate])
(defn add-order [order]
(swap! orders-queue conj order))
(defn cook-order [] (swap! orders-queue pop))
(add-order (->Order "Soup" "Fish&Chips"))
(pprint orders-queue)
(add-order (->Order "Salad" "Hamburger"))
(pprint orders-queue)
(add-order (->Order "Rice" "Steak"))
(pprint orders-queue)
(cook-order )
(pprint orders-queue)
Related
I have a small game that I'm writing in Clojure. I am very new to the language and one of the first errors I've stumbled upon is
Call to clojure.core/defn did not conform to spec.
I have no idea what causes the error, but an exception is thrown with this:
(ns survival-text.core
(:gen-class))
(def health 10)
(def hunger 10)
(def locations ["plains" "ocean" "mountains" "forest"])
(def currentLocation (atom 0))
(defn walk
(def random rand-int (count locations))
(reset! currentLocation (atom random)))
(defn choices
[input]
(case (input)
"walk" (walk)
"exit" (System/exit 0)
""))
(defn -main
[& args]
(while true
(def input read-line)
(choices input)))
But not this:
(ns survival-text.core
(:gen-class))
(def health 10)
(def hunger 10)
(def locations ["plains" "ocean" "mountains" "forest"])
(def currentLocation (atom 0))
(defn choices
[input]
(case (input)
"walk" (reset! currentLocation (atom (rand-nth locations)))
"exit" (System/exit 0)
""))
(defn -main
[& args]
(while true
(def input read-line)
(choices input)))
This is what is being thrown at me
Exception in thread "main" Syntax error macroexpanding clojure.core/defn at (survival_text/core.clj:9:1).
Call to clojure.core/defn did not conform to spec.
at clojure.lang.Compiler.checkSpecs(Compiler.java:6971)
at clojure.lang.Compiler.macroexpand1(Compiler.java:6987)
at clojure.lang.Compiler.macroexpand(Compiler.java:7074)
at clojure.lang.Compiler.eval(Compiler.java:7160)
at clojure.lang.Compiler.load(Compiler.java:7635)
at clojure.lang.RT.loadResourceScript(RT.java:381)
at clojure.lang.RT.loadResourceScript(RT.java:372)
at clojure.lang.RT.load(RT.java:463)
at clojure.lang.RT.load(RT.java:428)
at clojure.core$load$fn__6824.invoke(core.clj:6126)
at clojure.core$load.invokeStatic(core.clj:6125)
at clojure.core$load.doInvoke(core.clj:6109)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5908)
at clojure.core$load_one.invoke(core.clj:5903)
at clojure.core$load_lib$fn__6765.invoke(core.clj:5948)
at clojure.core$load_lib.invokeStatic(core.clj:5947)
at clojure.core$load_lib.doInvoke(core.clj:5928)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$load_libs.invokeStatic(core.clj:5985)
at clojure.core$load_libs.doInvoke(core.clj:5969)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:667)
at clojure.core$require.invokeStatic(core.clj:6007)
at clojure.core$require.doInvoke(core.clj:6007)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at user$eval140$fn__144.invoke(form-init1707647033589713549.clj:1)
at user$eval140.invokeStatic(form-init1707647033589713549.clj:1)
at user$eval140.invoke(form-init1707647033589713549.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:7176)
at clojure.lang.Compiler.eval(Compiler.java:7166)
at clojure.lang.Compiler.load(Compiler.java:7635)
at clojure.lang.Compiler.loadFile(Compiler.java:7573)
at clojure.main$load_script.invokeStatic(main.clj:452)
at clojure.main$init_opt.invokeStatic(main.clj:454)
at clojure.main$init_opt.invoke(main.clj:454)
at clojure.main$initialize.invokeStatic(main.clj:485)
at clojure.main$null_opt.invokeStatic(main.clj:519)
at clojure.main$null_opt.invoke(main.clj:516)
at clojure.main$main.invokeStatic(main.clj:598)
at clojure.main$main.doInvoke(main.clj:561)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.main.main(main.java:37)
Caused by: clojure.lang.ExceptionInfo: Call to clojure.core/defn did not conform to spec. {:clojure.spec.alpha/problems ({:path [:fn-tail :arity-1 :params], :pred clojure.core/vector?, :val (def random rand-int (count locations)), :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/params+body :clojure.core.specs.alpha/param-list :clojure.core.specs.alpha/param-list], :in [1]} {:path [:fn-tail :arity-n :bodies :params], :pred clojure.core/vector?, :val def, :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/params+body :clojure.core.specs.alpha/params+body :clojure.core.specs.alpha/params+body :clojure.core.specs.alpha/param-list :clojure.core.specs.alpha/param-list], :in [1 0]}), :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__2509 0x41e68d87 "clojure.spec.alpha$regex_spec_impl$reify__2509#41e68d87"], :clojure.spec.alpha/value (walk (def random rand-int (count locations)) (reset! currentLocation (atom random))), :clojure.spec.alpha/args (walk (def random rand-int (count locations)) (reset! currentLocation (atom random)))}
at clojure.spec.alpha$macroexpand_check.invokeStatic(alpha.clj:705)
at clojure.spec.alpha$macroexpand_check.invoke(alpha.clj:697)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Var.applyTo(Var.java:705)
at clojure.lang.Compiler.checkSpecs(Compiler.java:6969)
... 44 more
I am using leiningen and I haven't changed anything an all apart from the src/survival-text/core.clj
What causes the error and how do I fix it?
You forgot the args list. It should look like:
(defn walk
[] ; no args to this function
(let [random (rand-int (count locations))]
(reset! currentLocation random)))
You may also want to review Clojure intro material like Brave Clojure or Getting Clojure for an introduction. In particular, it is generally not a good idea to use (def ...) inside of a function.
i have one file "map_reduce2.clj" and another "map_reduce3.clj", the both defin function "map-reduce" themself.
now i want to use namespace of "map_reduce2.clj" in "map_reduce3.clj", but when i press "C-c C-k" in emacs to compile the "map_reduce3.clj",
error happens: "parse-line already refers to: #'chapter12.map-reduce2/parse-line in
namespace: chapter12.map-reduce3" , but this doesn't make any sense.
; map_reduce3.cli
(ns chapter12.map-reduce3
(:use clojure.java.io)
(:require [chapter12.map-reduce2 :as c12]))
(def IGNORE "_")
(defn parse-line [line]
(let [tokens (.split (.toLowerCase line) " ")]
[[IGNORE (count tokens)]]))
(defn average [numbers]
(/ (apply + numbers)
(count numbers)))
(defn reducer [combined]
(average (val (first combined))))
(defn average-line-length [filename]
(c12/map-reduce parse-line reducer (line-seq (reader filename))))
; map_reduce2.clj
(ns chapter12.map-reduce2
(:use clojure.java.io))
(defn combine [mapped]
(->> (apply concat mapped)
(group-by first)
(map (fn [[k v]]
{k (map second v)}))
(apply merge-with conj)))
(defn map-reduce [mapper reducer args-seq]
(->> (map mapper args-seq)
(combine)
(reducer)))
(defn parse-line [line]
(let [tokens (.split (.toLowerCase line) " ")]
(map #(vector % 1) tokens)))
(defn sum [[k v]]
{k (apply + v)})
(defn reduce-parsed-lines [collected-values]
(apply merge (map sum collected-values)))
(defn word-frequency [filename]
(map-reduce parse-line reduce-parsed-lines (line-seq (reader filename))))
images of the the error
This probably means you have dirty REPL state. Maybe you moved the function parse-line from one namespace to the other. I suggest restarting the REPL, or unload parse-line from map-reduce3: How to unload a function from another namespace?.
Here is an example from joy of clojure chapter 8:
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~#(mapcat (fn [[k v]]
[k `'~v])
ctx)]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a 1 b 2} '(+ a b)))
I find the ``'` part quite perplexing, what's it for?
I also tried to modify the function a bit:
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~#(mapcat (fn [[k v]]
[k `~v])
ctx)]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a 1 b 2} '(+ a b)))
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~#(vec (apply
concat
ctx))]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a 1 b 2} '(+ a b)))
All the versions above have similar effect. Why did the author choose to use `' then?
A more detailed look:
(use 'clojure.pprint)
(defmacro epprint [expr]
`(do
(print "==>")
(pprint '~expr)
(pprint ~expr)))
(defmacro epprints [& exprs]
(list* 'do (map (fn [x] (list 'epprint x))
exprs)))
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~#(mapcat (fn [[k v]]
(epprints
(class v)
v
(class '~v)
'~v
(class `'~v)
`'~v
(class ctx)
ctx)
[k `~v])
ctx)]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a (* 2 3) b (inc 11)} '(+ a b)))
This prints out the following in the repl:
==>(class v)
clojure.lang.PersistentList
==>v
(* 2 3)
==>(class '~v)
clojure.lang.PersistentList
==>'~v
~v
==>(class
(clojure.core/seq
(clojure.core/concat
(clojure.core/list 'quote)
(clojure.core/list v))))
clojure.lang.Cons
==>(clojure.core/seq
(clojure.core/concat (clojure.core/list 'quote) (clojure.core/list v)))
'(* 2 3)
==>(class ctx)
clojure.lang.PersistentArrayMap
==>ctx
{a (* 2 3), b (inc 11)}
==>(class v)
clojure.lang.PersistentList
==>v
(inc 11)
==>(class '~v)
clojure.lang.PersistentList
==>'~v
~v
==>(class
(clojure.core/seq
(clojure.core/concat
(clojure.core/list 'quote)
(clojure.core/list v))))
clojure.lang.Cons
==>(clojure.core/seq
(clojure.core/concat (clojure.core/list 'quote) (clojure.core/list v)))
'(inc 11)
==>(class ctx)
clojure.lang.PersistentArrayMap
==>ctx
{a (* 2 3), b (inc 11)}
==>new-expr
(clojure.core/let [a (* 2 3) b (inc 11)] (+ a b))
18
Again, using a single syntax quote for v seems to get the job done.
In fact, using `'v might cause you some trouble:
(defn contextual-eval [ctx expr]
(let [new-expr
`(let [~#(mapcat (fn [[k v]]
[k `'~v])
ctx)]
~expr)]
(pprint new-expr)
(eval new-expr)))
(pprint (contextual-eval '{a (inc 3) b (* 3 4)} '(+ a b)))
CompilerException java.lang.ClassCastException: clojure.lang.PersistentList cannot be cast to java.lang.Number, compiling:(/Users/kaiyin/personal_config_bin_files/workspace/typedclj/src/typedclj/macros.clj:14:22)
`'~v is a way to return
(list 'quote v)
in this case quoting the actual value of v in the let expression, not the symbol itself.
IDK The Joy Of Clojure, but apparently the authors want to prevent forms passed in ctx from being evaluated in the expanded let form. E. g. (contextual-eval '{a (+ 3 4)} 'a) will return (+ 3 4) but 7 in your versions which are both identical in behavior.
Your modified versions have the same effect only because you're trying them on very simple data. Try instead with a mapping like {'a 'x}, a context in which the binding for a is the symbol x.
user> (defn contextual-eval [ctx expr]
(let [new-expr
`(let [~#(mapcat (fn [[k v]]
[k `'~v])
ctx)]
~expr)]
(eval new-expr)))
#'user/contextual-eval
user> (contextual-eval {'a 'x} '(name a))
"x"
user> (defn contextual-eval [ctx expr]
(let [new-expr
`(let [~#(mapcat (fn [[k v]]
[k `~v])
ctx)]
~expr)]
(eval new-expr)))
#'user/contextual-eval
user> (contextual-eval {'a 'x} '(name a))
; Evaluation aborted.
The problem is that in your version, by neglecting the quote, you are double-evaluating the values bound to your symbols: x shouldn't be evaluated, because the value is actually the symbol x. You get away with this double evaluation in your simple test cases, because 1 evaluates to itself: (eval (eval (eval 1))) would work fine too. But doing that with most data structures is wrong, because they have non-trivial evaluation semantics.
Note also that the following expressions are identical in all cases, so there's never a reason to write any of them but the first one:
x
`~x
`~`~x
```~`~~`~~x
If you syntax-quote and then immediately un-quote, you haven't accomplished anything. So, if you ever find yourself writing a quote followed by an unquote, this should be a big red flag that you are doing something wrong.
I know how to extract one attribute using zip-xml/attr, but how to extract multiple attributes?
e.g I have the following
<table>
<column name="col1" type="varchar" length="8"/>
<column name="col2" type="varchar" length="16"/>
<column name="col3" type="int" length="16"/>
<table>
And the expected result is. A silly way is to call zip-xml/attr for each attribute, but is there any elegant way to do that?
[["co11" "varchar" 8] [["co12" "varchar" 16] [["co13" "int" 16]
My advice is to use a tree-walking function to extract the interesting data from the XML tree. clojure.walk has several of these, but here I use tree-seq from core clojure to just produce a seq of nodes and work on that. This function takes two functions - a branch? predicate which checks if a node can have children and a children function which gets them. I use :content for both, as tags with no nested tags produce nil, which is a falsey value and so it works also as a predicate.
(->> (clojure.xml/parse "res/doc.xml") ;;source file for your xml
(tree-seq :content :content) ;; Produce a seq by walking the tree
(filter #(= :column (:tag %))) ;;Take only :column tags
(mapv (comp vec vals :attrs)))
;;Collect the values of the :attrs maps into vectors
;;and collect those into a vector with mapv
Your desired output had unmatched square brackets, but I assume it should be like
[["col1" "varchar" "8"] ["col2" "varchar" "16"] ["col3" "int" "16"]]
which was my return value. However, this is potentially brittle - you're relying on the maps returned by clojure.xml/parse preserving the ordering of the attributes in the XML in order to know what the data means. That's not really part of the contract of maps. As an implementation detail it creates clojure.lang.PersistentStructMaps which apparently do have this feature, but it might not always be so.
Alternatively you could use just (mapv :attrs) to keep the whole of the map in there.
The right solution depends on how large and complex the XML is and to some extent, what you know about its structure. If it needs to be very generic, then you need to have quite a lot of logic to navigate the nodes etc. However, if it is a known format and you know what nodes you are interested in, its pretty straight-forward.
I used clojure.zip to create a zipper from the XML file and then use clojure.data.zip.xml to extract the nodes/paths I was interested in. I then defined simple helper functions to process specific nodes. This was pretty much my first bit of clojure and I've not yet gone back to it to re-factor it and refine/clarify some of my very rough clojure idioms based on what I've learnt since, but in the spirit of an example being worth 1000 words, here it is -
(ns arcis.models.nessus
(:use [taoensso.timbre :only [trace debug info warn error fatal]])
(:require [arcis.util :as util]
[arcis.models.db :as db]
[clojure.java.io :as io]
[clojure.xml :as xml]
[clojure.zip :as zip]
[clojure.data.zip.xml :as zx]))
(def nessus-host-keys [:hostname :host_fqdn
:system_type :operating_system
:operating_system_unsupported])
(def used-nessus-host-keys (conj nessus-host-keys
:host_start :host_end
:items :traceroute_hop_0 :traceroute_hop_1
:traceroute_hop_2 :traceroute_hop_3
:traceroute_hop_4 :traceroute_hop_5
:traceroute_hop_6 :traceroute_hop_7
:traceroute_hop_8 :traceroute_hop_9
:traceroute_hop_10 :traceroute_hop_11
:traceroute_hop_12 :traceroute_hop_13
:traceroute_hop_14 :traceroute_hop_15
:traceroute_hop_16 :traceroute_hop_17
:host_ip :patch_summary_total_cves
:cpe_0 :cpe_1 :cpe_2 :cpe_3 :cpe_4 :cpe_5
:cpe_6 :cpe_7 :cpe_8 :cpe_9))
(def nessus-item-keys [:port :svc_name :protocol :severity :plugin_id
:plugin_output])
(def used-nessus-item-keys (conj nessus-item-keys
:plugin_details
:plugin_name
:plugin_family))
(def nessus-plugin-keys [:plugin_id :plugin_name :plugin_family :fname
:script_version :plugin_type :exploitability_ease
:vuln_publication_date :cvss_temporal_data
:solution :cvss_temporal_score :risk_factor
:description :cvss_vector :synopsis
:patch_publication_date :exploit_available
:plugin_publication_date :plugin_modification_date
:cve :bid :exploit_framework_canvas :edb_id
:exploit_framework_metasploit :exploit_framework_core
:metasploit_name :canvas_package :osvdb :cwe
:cvss_temporal_vector :cvss_base_score :cpe
:exploited_by_malware])
(def used-nessus-plugin-keys (conj nessus-plugin-keys
:xref :see_also :cert
:attachment :iava :stig_severity :hp
:secunia :iawb :msft))
(def show-unprocessed true)
(defn log-unprocessed [title vls]
(if (and show-unprocessed
(seq vls))
(println (str "Unprocessed " title ": " vls))))
;;; parse nessus report
(defn parse-xref [xref]
{:xref (first (:content xref))})
(defn parse-see-also [see-also]
{:see_also (first (:content see-also))})
(defn parse-plugin [plugin]
{(util/db-keyword (name (:tag plugin))) (first (:content plugin))})
(defn parse-contents [cont]
(let [xref (mapv parse-xref (filter #(= (:tag %) :xref) cont))
see-also (mapv parse-see-also (filter #(= (:tag %) :see-also) cont))
details (reduce merge {}
(map parse-plugin
(remove #(or (= (:tag %) :xref)
(= (:tag %) :see-also)) cont)))]
(assoc details
:see_also see-also
:xref xref)))
(defn fix-item-keywords [item]
(let [ks (keys item)]
(into {}
(for [k ks]
[(util/db-keyword (name k))
(k item)]))))
(defn parse-item [item]
(let [attrs (fix-item-keywords (:attrs item))
contents (parse-contents (:content item))]
(assoc attrs
:plugin_output (:plugin_output contents)
:plugin_details (assoc (dissoc contents :plugin_output)
:plugin_id (:plugin_id attrs)
:plugin_family (:plugin_family attrs)))))
(defn parse-properties [props]
(into {}
(for [p props]
[(util/db-keyword (:name (:attrs p)))
(first (:content p))])))
(defn parse-host [h]
(let [items (map first (zx/xml-> h :ReportItem))
properties (:content (first (zx/xml1-> h :HostProperties)))]
(assoc (parse-properties properties)
:hostname (zx/attr h :name)
:items (mapv parse-item items))))
(defn parse-hosts [hosts]
(mapv parse-host hosts))
(defn parse-file [f]
(let [root (zip/xml-zip (xml/parse (io/file f)))
report-xml (zx/xml1-> root :Report)
hosts (zx/xml-> report-xml :ReportHost)]
{:report_name (zx/attr report-xml :name)
:policy (zx/text (zx/xml1-> root :Policy :policyName))
:hosts (parse-hosts hosts)}))
;;; insert nessus records into db
(defn mk-host-rec [scan-id host]
(let [[id err] (db/get-sequence-nextval "host_seq")]
(if (nil? err)
(assoc (util/build-map host nessus-host-keys)
:ipv4 (:host_ip host)
:scan_start (util/from-nessus-date (:scan_start host))
:scan_end (util/from-nessus-date (:scan_end host))
:total_cves (:patch_summary_total_cves host)
:id id
:scan_id scan-id)
nil)))
(defn insert-patches [p]
(when (seq p)
(db/insert-nessus-host-patch (first p))
(recur (rest p))))
(defn insert-host-patch [id host]
(let [p-keys (filter #(re-find #"patch_summary_*" %) (map name (keys host)))
recs (map (fn [s]
{:id (first (db/get-sequence-nextval "patch_seq"))
:host_id id
:summary ((keyword (str "patch_summary_txt_" s)) host)
:cve_num ((keyword (str "patch_summary_cve_num_" s)) host)
:cves ((keyword (str "patch_summary_cves_" s)) host)})
(filter seq
(map #(second (re-find #"patch_summary_txt_(.*)" %))
p-keys)))]
(insert-patches recs)
(util/remove-keys host (map keyword p-keys))))
(defn mk-item-rec [host-id item]
(let [[id err] (db/get-sequence-nextval "item_seq")]
(assoc (util/build-map item nessus-item-keys)
:host_id host-id
:id id)))
(defn insert-item [host-id item]
(let [rec (mk-item-rec host-id item)
not-done (keys (util/remove-keys item used-nessus-item-keys))]
(log-unprocessed "Item Keys" not-done)
(db/insert-nessus-report-item rec)
(:plugin_id item)))
(defn mk-plugin-rec [item]
(let [rec (util/build-map (:plugin_details item) nessus-plugin-keys)
not-used (keys (util/remove-keys (:plugin_details item)
used-nessus-plugin-keys))]
(log-unprocessed "Plugin Keys" not-used)
(assoc rec
:vuln_publication_date (util/from-nessus-date
(:vuln_publication_date rec))
:patch_publication_date (util/from-nessus-date
(:patch_publication_date rec))
:plugin_publication_date (util/from-nessus-date
(:plugin_publication_date rec))
:plugin_modification_date (util/from-nessus-date
(:plugin_modificaiton_date rec)))))
(defn insert-xref [plugin-id xrefs]
(when (seq xrefs)
(let [xref {:id (first (db/get-sequence-nextval "xref_seq"))
:plugin_id plugin-id
:xref (:xref (first xrefs))}]
(db/insert-nessus-xref xref)
(recur plugin-id (rest xrefs)))))
(defn insert-see-also [plugin-id see-also]
(when (seq see-also)
(let [sa {:id (first (db/get-sequence-nextval "ref_seq"))
:plugin_id plugin-id
:reference (:see_also (first see-also))}]
(db/insert-nessus-ref sa)
(recur plugin-id (rest see-also)))))
(defn insert-plugin [item]
(let [rec (mk-plugin-rec item)
xref (:xref (:plugin_details item))
see-also (:see_also (:plugin_details item))]
(if (seq xref)
(insert-xref (:plugin_id rec) xref))
(if (seq see-also)
(insert-see-also (:plugin_id rec) see-also))
(db/upsert-nessus-plugin rec)))
(defn insert-items [host-id items plugin-set]
(if (empty? items)
plugin-set
(let [p (insert-item host-id (first items))]
(if-not (contains? plugin-set p)
(insert-plugin (first items)))
(recur host-id (rest items) (conj plugin-set p)))))
(defn insert-host [scan-id host plugin-set]
(if-let [h-rec (mk-host-rec scan-id host)]
(let [[v err] (db/insert-nessus-host h-rec)
items (:items host)]
(if (nil? err)
(let [host2 (insert-host-patch (:id h-rec) host)]
(log-unprocessed "Host Keys" (keys (util/remove-keys
host2 used-nessus-host-keys)))
(insert-items (:id h-rec) items plugin-set))
plugin-set))
plugin-set))
(defn insert-hosts
([id hosts]
(insert-hosts id hosts #{}))
([id hosts plugins]
(if (empty? hosts)
plugins
(let [plugin-set (insert-host id (first hosts) plugins)]
(recur id (rest hosts) plugin-set)))))
(defn mk-scan-record [id report]
{:id id
:name (:report_name report)
:scan_dt (util/to-sql-date)
:policy (:policy report)
:entered_dt (util/to-sql-date)})
(defn store-report [update-plugins report]
(let [[id err] (db/get-sequence-nextval "nscan_seq")
scan-rec (mk-scan-record id report)]
(if (nil? err)
(let [[v e] (db/insert-nessus-scan scan-rec)]
(if (nil? e)
(if update-plugins
(let [plugin-list (set (first (db/select-nessus-plugin-ids)))]
[(insert-hosts id (:hosts report) plugin-list) nil])
[(insert-hosts id (:hosts report)) nil])
[v e]))
[id err])))
(defn process-nessus-report [update-plugins filename]
(let [report (parse-file filename)]
(println (str "Report: " (:report_name report)
"\nPolicy: " (:policy report)
"\nHost Records: " (count (:hosts report))))
(store-report update-plugins report)))
Magos's answer using tree-seq is perfectly fine, but there's no reason to abandon zippers; filtering using zippers is more succinct and the arguably the "clojure" way. (note this example uses data.xml ([org.clojure/data.xml "0.0.8"]) instead of clojure.xml).
(require '[clojure.data.zip.xml :as zf])
(require '[clojure.zip :as z])
(def ex
"<table>
<column name=\"col1\" type=\"varchar\" length=\"8\"/>
<column name=\"col2\" type=\"varchar\" length=\"16\"/>
<column name=\"col3\" type=\"int\" length=\"16\"/>
</table>")
(let [x (z/xml-zip (clojure.data.xml/parse-str ex))]
(->> (zf/xml-> x :column) ;;equivalent to (->> treeseq ... filter)
flatten
(keep :attrs)
(map vals)))
;>>> (("col1" "varchar" "8") ("col2" "varchar" "16") ("col3" "int" "16"))
But the xml-> macro simply applies functions in order, so you can do the following:
(let [x (z/xml-zip (clojure.data.xml/parse-str ex))]
(->> (zf/xml-> x :column #(keep :attrs %))
(map vals)))
;>>> (("col1" "varchar" "8") ("col2" "varchar" "16") ("col3" "int" "16"))
In Clojure 1.5.0, how can I provide a custom pretty-printer for my own record type, defined with defrecord.
(defrecord MyRecord [a b])
(defmethod print-method MyRecord [x ^java.io.Writer writer]
(print-method (:a x) writer))
(defmethod print-dup MyRecord [x ^java.io.Writer writer]
(print-dup (:a x) writer))
(println (MyRecord. 'a 'b)) ;; a -- OK
(clojure.pprint/pprint (MyRecord. 'a 'b)) ;; {:a a, :b b} -- not OK, I want a
I would like clojure.pprint/pprint to also use my cutsom printer (which now, should just pretty-prints whatever is in the field a of the record for illustration purposes).
clojure.pprint namespace uses different dispatch mechanisms than the clojure.core print functions. You need to use with-pprint-dispatch in order to customize pprint.
(clojure.pprint/with-pprint-dispatch print ;; Make the dispatch to your print function
(clojure.pprint/pprint (MyRecord. 'a 'b)))
To customize the simple dispatcher, add something like:
(. clojure.pprint/simple-dispatch addMethod MyRecord pprint-myrecord)
This has been very confusing, with correct answers to different parts of the problem spread around various places, so I put it all together in the hopes that someone might save some time.
I went through all the permutations of defining the multimethods print-method, print-dup, and simple-dispatch for the defrecord, and setting dynamic vars *print-pprint-dispatch* and *print-dup*. I put an example defrecord through both pr and pprint and came up with the following flowchart. It makes sense now I can see everything at once.
The permutation code is brute force; I just looked at the outputs and created the Visio diagram directly. Nothing fancy.
(ns print-test
(:require [clojure.pprint :as pp]))
(defrecord tr000 [val])
(defrecord tr001 [val])
(defrecord tr010 [val])
(defrecord tr011 [val])
(defrecord tr100 [val])
(defrecord tr101 [val])
(defrecord tr110 [val])
(defrecord tr111 [val])
;;(defmethod print-method tr000 [obj writer] (.write writer "tr000 print-method"))
;;(defmethod print-dup tr000 [obj writer] (.write writer "tr000 print-dup"))
;;(defmethod pp/simple-dispatch tr000 [obj] (.write *out* "tr000 simple-dispatch"))
;;(defmethod print-method tr001 [obj writer] (.write writer "tr001 print-method"))
;;(defmethod print-dup tr001 [obj writer] (.write writer "tr001 print-dup"))
(defmethod pp/simple-dispatch tr001 [obj] (.write *out* "tr001 simple-dispatch"))
;;(defmethod print-method tr010 [obj writer] (.write writer "tr010 print-method"))
(defmethod print-dup tr010 [obj writer] (.write writer "tr010 print-dup"))
;;(defmethod pp/simple-dispatch tr010 [obj] (.write *out* "tr010 simple-dispatch"))
;;(defmethod print-method tr011 [obj writer] (.write writer "tr011 print-method"))
(defmethod print-dup tr011 [obj writer] (.write writer "tr011 print-dup"))
(defmethod pp/simple-dispatch tr011 [obj] (.write *out* "tr011 simple-dispatch"))
(defmethod print-method tr100 [obj writer] (.write writer "tr100 print-method"))
;;(defmethod print-dup tr100 [obj writer] (.write writer "tr100 print-dup"))
;;(defmethod pp/simple-dispatch tr100 [obj] (.write *out* "tr100 simple-dispatch"))
(defmethod print-method tr101 [obj writer] (.write writer "tr101 print-method"))
;;(defmethod print-dup tr101 [obj writer] (.write writer "tr101 print-dup"))
(defmethod pp/simple-dispatch tr101 [obj] (.write *out* "tr101 simple-dispatch"))
(defmethod print-method tr110 [obj writer] (.write writer "tr110 print-method"))
(defmethod print-dup tr110 [obj writer] (.write writer "tr110 print-dup"))
;;(defmethod pp/simple-dispatch tr110 [obj] (.write *out* "tr110 simple-dispatch"))
(defmethod print-method tr111 [obj writer] (.write writer "tr111 print-method"))
(defmethod print-dup tr111 [obj writer] (.write writer "tr111 print-dup"))
(defmethod pp/simple-dispatch tr111 [obj] (.write *out* "tr111 simple-dispatch"))
(def t000 (->tr000 10))
(def t001 (->tr001 20))
(def t010 (->tr010 30))
(def t011 (->tr011 40))
(def t100 (->tr100 50))
(def t101 (->tr101 60))
(def t110 (->tr110 70))
(def t111 (->tr111 80))
(def recs [t000 t001 t010 t011 t100 t101 t110 t111])
(def dt (java.time.LocalTime/now))
(defmethod print-dup java.time.LocalTime [obj writer] (.write writer "datetime_dup"))
(println "(pr ...) outputs the following")
(doseq [rec recs]
(doseq [prppd [nil #(.write *out* "pprint-dispatch with" %)]]
(binding [pp/*print-pprint-dispatch*
(if prppd #(.write *out* (str "pprint-dipatch-with" %))
pp/*print-pprint-dispatch*)]
(doseq [pd [false true]]
(binding [*print-dup* pd]
(let [ppdstr (format "%6s" (boolean pp/*print-pprint-dispatch*))
dupstr (format "%6s" *print-dup*)
outstr (pr-str rec)]
(binding [*print-dup* false]
(println ppdstr dupstr ":" outstr))))))))
(println "\n(pprint ...) outputs the following")
(doseq [rec recs]
(doseq [prppd [false true]]
(binding [pp/*print-pprint-dispatch*
(if prppd #(.write *out* (str "pprint-dipatch-with" %))
pp/*print-pprint-dispatch*)]
(doseq [pd [false true]]
(binding [*print-dup* pd]
(let [outstr
(with-out-str
(pp/pprint {:ppdstr prppd
:dupstr pd
:dt dt
:strobj rec}))]
(binding [*print-dup* false]
(println outstr "\n"))))))))
Maybe not ideal, but I haven't found better than pr and pr-str.
Example REPL session :
(ns my-ns)
(defprotocol Foo
(bazfn [this]))
(defrecord Bar [a]
Foo
(bazfn [this] 123))
(pr-str (Bar. "ok")) ;;=> "#my_ns.Bar{:a \"ok\"}"
(pr (Bar. "ok")) ;; prints the same as above