08: visiting functions
You now have most of the pieces you need to generate and insert fixture data into a test database. Now you just need to... actually insert the data! To insert data you must visit each ent in the ent db with a visiting function, and that's what you'll learn to do in this section and the next.
Earlier I wrote,
Visiting ent nodes is kind of like mapping: when you call
map
on a seq, you apply a mapping function to each element, creating a new seq from the mapping function's return values. When you visit ents, you apply a visiting function to each ent. The visiting function's return value is stored as an attribute on the ent (remember that ents are implemented as graph nodes, and nodes can have attributes).
You've actually already seen a visiting function at work. In the last couple sections you learned how to use spec to generate a value for each ent. The generated value was stored under the :spec-gen
attribute; that's because the sg/ent-db-spec-gen
you called actually applies a visiting function to the ent db it generates. Let's create our own visiting function so you can see how this works:
(ex-01)
creates an ent db, applies a visiting function, and then looks up the :attrs
key in the graph associated with the ent db's :data
key. The :attrs
key is where loom stores each node's attributes. We can see that each ent (:u0
, :t0
, :tl0
) has an attribute with the key :announce
and the value of "announcing... :ent-name!"
. Let's walk through this. You call the function ex-01
, whose body is:
sm/add-ents
builds the ent db and passes it to sm/visit-ents
. sm/visit-ents
takes three arguments: the ent db, a visit key (:announce
), and a visiting function. Then, internally, sm/visits-ents
iterates overs each ent in the ent db, passing the ent's name to the visiting function along with the db and visit key, using the return value to assign an attribute to the ent.
So in the above example, the ents are :u0
, :t0
, and :p0
, and sm/visit-ents
iterates over them in that order. For :u0
, it passes the following arguments to the visiting function announce
:
the ent db
a map that includes the key
:ent-name
whose value is:u0
The function announce
uses the :ent-name
passed in the second argument to return the string "announcing... :u0!"
, and that gets associated with the :announce
key under the ent's attributes.
So, it's great and all that the announce
function returns its little string. But how would we use visiting functions to actually insert records into a database? The answer is that the map passed as the second argument to visiting functions contains a lot more information than just the :ent-name
of the current ent. It also includes the :ent-type
(:user
, :topic
, :post
) and, when you're using the clojure.spec visiting function, the :spec-gen
value. The next section will show you how to combine those values to insert data in a database.
Before you continue to the next section, though, try implementing your own visiting function. Can you write a visiting function that calls swap
on atom, inserting a vector of [:ent-type :ent-name]
for every ent?
Last updated