From a542227442f71ec127c9c49ecc704d40f75b5fe9 Mon Sep 17 00:00:00 2001 From: Bob Date: Sat, 3 Jul 2021 17:53:58 -0400 Subject: [PATCH] add examples for #324 and #369 [skip ci] (#911) * add examples for: - checking stdin for available input - using data.xml lib * fix some string contents left over from an XML change --- examples/README.md | 29 +++++++++++++++ examples/xml-example.clj | 78 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 examples/xml-example.clj diff --git a/examples/README.md b/examples/README.md index 330d8757..7c958753 100644 --- a/examples/README.md +++ b/examples/README.md @@ -32,6 +32,9 @@ - [digitalocean-ping.clj](#digitalocean-pingclj) - [download-aliases.clj](#download-aliasesclj) - [Is TTY?](#is-tty) + - [normalize-keywords.clj](#normalize-keywordsclj) + - [Check stdin for data](#check-stdin-for-data) + - [Using org.clojure/data.xml](#using-orgclojuredataxml) Here's a gallery of useful examples. Do you have a useful example? PR welcome! @@ -483,3 +486,29 @@ $ bb examples/normalize-keywords.clj /tmp/test.clj [:clojure.set/foo :test/bar] ``` + +## Check stdin for data + +```shell +# when piping something in, we get a positive number +$ echo 'abc' | bb '(pos? (.available System/in))' +true +# even if we echo an empty string, we still get the newline +$ echo '' | bb '(pos? (.available System/in))' +true +# with nothing passed in, we finally return false +$ bb '(pos? (.available System/in))' +false +``` + +## Using org.clojure/data.xml + +[xml-example.clj](xml-example.clj) explores some of the capabilities provided +by the `org.clojure/data.xml` library (required as `xml` by default in Babashka). +While running the script will show some output, reading the file shows the library +in use. + +```shell +$ bb examples/xml-example.clj +... some vaguely interesting XML manipulation output +``` diff --git a/examples/xml-example.clj b/examples/xml-example.clj new file mode 100644 index 00000000..8aa28dbd --- /dev/null +++ b/examples/xml-example.clj @@ -0,0 +1,78 @@ +; let's build up a little data structure to play with + +(def pet-store-sexp + [:pet-store + [:family + [:owners + [:name "Terry Smith"] + [:name "Sam Smith"] + [:phone "555-1212"]] + [:animals + [:animal {:type "dog"} "Sparky"]]] + [:family + [:owners + [:name "Pat Jones"] + [:phone "555-2121"]] + [:animals + [:animal {:type "hamster"} "Oliver"] + [:animal {:type "cat"} "Kat"]]]]) + +; we can build XML from this + +(def xml-str (xml/indent-str (xml/sexp-as-element pet-store-sexp))) + +(println "Our XML as a string is:") +(println xml-str) + +(comment xml-str is + " + + + + Terry Smith...") + +; and then we can parse that XML back into a data structure + +(def xml-tree (xml/parse-str xml-str)) + +#_"xml-tree is a nested associative structure: + {:tag :pet-store, + :attrs {}, + :content + ({:tag :family, + :attrs {}, + :content ...})}" + + +; with a couple of quick helper functions... + +(defn get-by-tag + "takes a seq of XML elements (or a 'root-ish' element) and a tag, filters by tag name, and gets the content of each" + [elems tag-name] + ; if we get (presumably) a root element, wrap it in a vector so we can still + ; filter by its tag + (if (xml/element? elems) + (recur [elems] tag-name) + (->> (filter #(= (:tag %) tag-name) elems) + (mapcat :content)))) + +(defn get-in-by-tag + "takes a seq of XML elements and a vector of tags, and drills into each + element by the tags, sort of like a mash-up of core/get-in and an XPath + lookup" + [elems tag-vec] + (reduce get-by-tag elems tag-vec)) + +; we can do things like... + +(println "all the owner names:" (get-in-by-tag + xml-tree + [:pet-store :family :owners :name])) + +(println "all the animal names:" (get-in-by-tag + xml-tree + [:pet-store :family :animals :animal])) + +(println "all the phone numbers:" (get-in-by-tag + xml-tree + [:pet-store :family :owners :phone]))