In the clojurescript re-frame todomvc application we find the following snippet in the todomvc.views namespace.
(defn todo-list
[visible-todos]
[:ul.todo-list
(for [todo #visible-todos]
^{:key (:id todo)} [todo-item todo])])
Although I have read the Clojure chapter on metadata I don't quite understand the purpose of:
^{:key
in the snippet above. Please explain.
The :key is what React needs when you have many items, so that they can be unique within the group. But the latest version of React does not need these keys. So if you use the latest versions of reframe / Reagent, just try without the :key metadata.
This metadata is equivalent to placing :key within the component. So for example what you have is equivalent to:
[todo-item {:key (:id todo)} todo]
Using the metadata approach is a convenience, which must in some cases be easier than the 'first key in props passed to the component' approach.
Here's more explanation.
^{:key (:id todo)} [todo-item todo] would be equivalent to (with-meta [todo-item todo] {:key (:id todo)}), see https://clojuredocs.org/clojure.core/with-meta
Reagent uses this to generate the corresponding react component with a key. Keys help React identify which items have changed, are added, or are removed. here is the explanation: https://reactjs.org/docs/lists-and-keys.html
Related
How can I list all the routes on a handler function? I'm looking for behavior similar to rails' rake routes. For example:
(defroutes foo-routes
(GET "/foo/:foo-id"
[foo-id]
"bar response")
(GET "/bar/:bar-id"
[bar-id]
"foo response"))
Is it then possible to extract a map from foo-bar-routes containing the following?
{:GET "/foo/:foo-id"
:GET "/bar/:bar-id"}
I don't think it is possible. defroutes is a macro that returns a ring handler. GET is a macro that returns a route. Route is again just a function that calls related handler only if method and path are matching. So in the end your foo-routes is just a clojure function that is composed of other functions defined by your routes and it doesn't maintain such map. If you need to get such map, maybe you can maintain it in your code yourself and generate routes out of this map.
I know this thread is quite old but I had the same question and could resolve it by myself, here's what I've got:
Assuming you defined your API this way:
(def my-api (compojure.api.api/api ...))
Then you can easily list the routes you defined that way:
(->> (.-get-routes my-api {})
(map (juxt second first)))
I am generating Codox documentation for a Clojurescript webapp. Here's an example function that I'll use to demonstrate my issue:
(defn breadcrumbs
"Render Breadcrumbs"
[app owner]
(om/component
(let [crumbs (:breadcrumbs app)]
(dom/div #js {:id "breadcrumbs"}
(when (> (count crumbs) 0)
(apply dom/ol #js {:className "breadcrumb os-border-default"}
(om/build-all breadcrumb crumbs)))))))
The problem is that using om/component causes Codox to generate documentation for four additional "hidden" functions (presumably these are the IRender, IDidUpdate, etc functions that can be defined for a component...but I'm not sure). In the documentation these functions look like this:
->t6127
(->t6127 crumb breadcrumb meta6128)
->t6130
(->t6130 crumb breadcrumb meta6131)
->t6133
(->t6133 owner app breadcrumbs meta6134)
->t6136
(->t6136 owner app breadcrumbs meta6137)
This unneeded documentation is greatly cluttering up the final product. I know individual functions can be skipped via "^:no-doc" but there doesn't seem to be a way to use that here.
Has anyone else experienced this and know how to get rid of the clutter?
Codox currently has problems with reify in ClojureScript, which is used internally by om/component.
David Nolen suggested that the information to distinguish these temporary values should be available in current versions of ClojureScript via the analyzer, but I haven't been able to find it, and no-one's been able to point me to it. See issue #72 on the Codox project page for more information.
om/component is a very simple macro that generates only render for IRender. It looks like Codox is giving you two functions for breadcumb (first two) and two for breadcumbs. One is probably the one you want and the other one is from render. You could either write your components like this:
(defn breadcrumbs
"Render Breadcrumbs"
[app owner]
(reify
om/IRender
(render ^:no-doc [_]
(let [crumbs (:breadcrumbs app)]
(dom/div #js {:id "breadcrumbs"}
(when (> (count crumbs) 0)
(apply dom/ol #js {:className "breadcrumb os-border- default"}
(om/build-all breadcrumb crumbs)))))))
or write your own no-doc-component macro:
(defmacro no-doc-component
[& body]
`(reify
om.core/IRender
(~(vary-meta 'render assoc :no-doc true) [this#]
~#body)))
Disclaimer: although I've tried the macro, I haven't tried it with Codox.
weevejester fixed this issue in version 0.8.11.
He also configured the ClojureScript analyzer to not analyze dependencies which allows me to generate docs for my Om projects. It should also reduce the number of namespaces in the :exclude configuration. Mine has been reduced to 0.
I tried to follow the documentation for clojure.instant/read-instant-timestamp, which reads:
clojure.instant/read-instant-timestamp
To read an instant as a java.sql.Timestamp, bind *data-readers* to a
map with this var as the value for the 'inst key. Timestamp preserves
fractional seconds with nanosecond precision. The timezone offset will
be used to convert into UTC.`
The following result was unexpected:
(do
(require '[clojure.edn :as edn])
(require '[clojure.instant :refer [read-instant-timestamp]])
(let [instant "#inst \"1970-01-01T00:00:09.999-00:00\""
reader-map {'inst #'read-instant-timestamp}]
;; This binding is not appearing to do anything.
(binding [*data-readers* reader-map]
;; prints java.util.Date -- unexpected
(->> instant edn/read-string class println)
;; prints java.sql.Timestamp -- as desired
(->> instant (edn/read-string {:readers reader-map}) class println))))
How can I use the *data-readers* binding? Clojure version 1.5.1.
clojure.edn functions by default only use data readers stored in clojure.core/default-data-readers which, as of Clojure 1.5.1, provides readers for instant and UUID literals. If you want to use custom readers, you can do that by passing in a :readers option; in particular, you can pass in *data-readers*. This is documented in the docstring for clojure.edn/read (the docstring for clojure.edn/read-string refers to that for read).
Here are some examples:
(require '[clojure.edn :as edn])
;; instant literals work out of the box:
(edn/read-string "#inst \"2013-06-08T01:00:00Z\"")
;= #inst "2013-06-08T01:00:00.000-00:00"
;; custom literals can be passed in in the opts map:
(edn/read-string {:readers {'foo identity}} "#foo :asdf")
;= :asdf
;; use current binding of *data-readers*
(edn/read-string {:readers *data-readers*} "...")
(The following section added in response to comments made by Richard Möhn in this GitHub issue's comment thread. The immediate question there is whether it is appropriate for a reader function to call eval on the data passed in. I am not affiliated with the project in question; please see the ticket for details, as well as Richard's comments on the present answer.)
It is worth adding that *data-readers* is implicitly populated from any data_readers.{clj,cljc} files that Clojure finds at the root of the classpath at startup time. This can be convenient (it allows one to use custom tagged literals in Clojure source code and at the REPL), but it does mean that new data readers may appear in there with a change to a single dependency. Using an explicitly constructed reader map with clojure.edn is a simple way to avoid surprises (which could be particularly nasty when dealing with untrusted input).
(Note that the implicit loading process does not result in any code being loaded immediately, or even when a tag mentioned in *data-readers* is first encountered; the process which populates *data-readers* creates empty namespaces with unbound Vars as placeholders, and to actually use those readers one still has to require the relevant namespaces in user code.)
The *data-readers* dynamic var seems to apply to the read-string and read functions from clojure.core only.
(require '[clojure.instant :refer [read-instant-timestamp]])
(let [instant "#inst \"1970-01-01T00:00:09.999-00:00\""
reader-map {'inst #'read-instant-timestamp}]
;; This will read a java.util.Date
(->> instant read-string class println)
;; This will read a java.sql.Timestamp
(binding [*data-readers* reader-map]
(->> instant read-string class println)))
Browsing through the source code for clojure.edn reader here, I couldn't find anything that would indicate that the same *data-readers* var is used at all there.
clojure.core's functions read and read-string use LispReader (which uses the value from *data-readers*), while the functions from clojure.edn use the EdnReader.
This edn library is relatively new in Clojure so that might be the reason why the documentation string is not specific enough regarding edn vs. core reader, which can cause this kind of confusion.
Hope it helps.
I apologize for a second question on the same topic, but I'm confused. Is there a Clojure module that follows lxml, even loosely, or how-to documentation on how to walk through an XML file using Clojure?
In Python, I can open an XML file using the lxml module; parse my way through the data; look for tags like <DeviceID>, <TamperName>, <SecheduledDateTime>, and then peform an action based on the value of one of those tags.
In Clojure, I have been given excellent answers on how to parse using data.xml and then further reduce the data.xml-parsed information by pulling out the :content tag's vals and putting the information in a tree-seq.
However, even that resultant data has other map tags embedded, which obviously do not respond to keys and vals functions.
I could take this data and use regular expression searches, but I feel I'm missing something much simpler.
The data right out of data.xml/parse (calling ret-xml-data) looks like this, using various (first parsed-xml) and other commands at the REPL:
[:tag :TamperExport]
[:attrs {}]
:content
#clojure.data.xml.Element{:tag :Header, :attrs {}, :content
(#clojure.data.xml.Element{:tag :ExportType, :attrs {},
:content ("Tamper Export")}
#clojure.data.xml.Element{:tag :CurrentDateTime,
:attrs {},
:content ("2012-06-26T15:40:22.063")} :attrs {},
:content ("{06643D9B-DCD3-459B-86A6-D21B20A03576}")}
Here is the Clojure code I have so far:
(defn ret-xml-data
"Returns a map of the supplied xml file, as parsed by data.xml/parse."
[xml-fnam]
(let [input-xml (try
(java.io.FileInputStream. xml-fnam)
(catch Exception e))]
(if-not (nil? input-xml)
(xmld/parse input-xml)
nil)))
(defn gen-xml-content-tree
"Returns a tree-seq with :content extracted."
[parsed-xml]
(map :content (first (tree-seq :content :content (:content parsed-xml)))))
I think I may have found a repeatable pattern to the data that will allow me to parse this without creating a hodgepodge:
xml-lib.core=> (first (second cl1))
#clojure.data.xml.Element{:tag :DeviceId, :attrs {}, :content ("80580608")}
xml-lib.core=> (keys (first (second cl1)))
(:tag :attrs :content)
xml-lib.core=> (vals (first (second cl1)))
(:DeviceId {} ("80580608"))
Thank you as always.
Edit:
Add some more testing.
The resulting data, if I ran through the tree-seq structure using a function like doseq, could probably now be parsed with actions taken.
First, it's hard to tell exactly what you're trying to do. When working on a programming problem, it helps both you and others helping you to have a "small case" you can present and solve before working towards a larger one.
From what it sounds like, you're trying to pull the content out of certain elements and perform actions based on that content.
I put together a small XML file with some simple content to try things out on:
<root>
<someele>
<item1>data</item1>
<deeper>
<item2>else</item2>
</deeper>
</someele>
</root>
I designed it to be what I think is representative of some of the core challenges with the problem at hand - in particular, being able to do stuff at arbitrary levels of nesting in the XML.
Looking at the wonderful Clojure Cheatsheet, I found xml-seq, and tried running it on the clojure.data.xml/parsed xml. The sequence went through each of the elements and then their children, making it easy to iterate over the XML.
To pick out and work with particular items in a sequence, I like using for loops with :when. :when makes it easy to just enter the body of the loop when certain conditions are true. I also make use of the "set as a function" semantics, which checks to see if something is in the set.
(for [ele (xml-seq (load-xml))
:when (#{:item1 :item2} (:tag ele))]
[(:tag ele) (first (:content ele))])
This gets back a sequence of ([:item1 "data"] [:item2 "else"]) that can then easily be acted on in other ways.
One of the key things to try and keep in mind about Clojure is that you tend to not need any special API to do stuff - the core language makes it easy to do most, if not all, that you need to do. Records (which are what you see being returned) are also maps for example, so assoc, dissoc, and so on work on them, and it's how they are intended to be worked with.
If this doesn't help you get to what you need, then could you provide a small sample output and a sample result that you want?
The closest Clojure library I can think of for lxml after a (very) brief look is called Enlive. It's listed as an HTML templating tool, but I'm pretty sure the techniques it uses for picking out HTML elements can also be applied to XML.
I'm developing a web application with Clojure, currently with Ring, Moustache, Sandbar and Hiccup. I have a resource named job, and a route to show a particular step in a multi-step form for a particular job defined like this (other routes omitted for simplicity):
(def web-app
(moustache/app
;; matches things like "/job/32/foo/bar"
:get [["job" id & step]
(fn [req] (web.controllers.job/show-job id step))]))
In the view my controller renders, there are links to other steps within the same job. At the moment, these urls are constructed by hand, e.g. (str "/job/" id step). I don't like that hard-coded "/job/" part of the url, because it repeats what I defined in the moustache route; if I change the route I need to change my controller, which is a tighter coupling than I care for.
I know that Rails' routing system has methods to generate urls from parameters, and I wish I had similar functionality, i.e. I wish I had a function url-for that I could call like this:
(url-for :job 32 "foo" "bar")
; => "/job/32/foo/bar"
Is there a Clojure web framework that makes this easy? If not, what are your thoughts on how this could be implemented?
Noir provides something similar. It's even called url-for.
The example function you have mentioned could be implemented as below. But I am not sure if this is exactly what you are looking for.
(defn url-for [& rest]
(reduce
#(str %1 "/" %2) "" (map #(if (keyword? %1) (name %1) (str %1)) rest)))