> For the complete documentation index, see [llms.txt](https://sweet-tooth.gitbook.io/specmonstah/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sweet-tooth.gitbook.io/specmonstah/tutorial/08-visiting-functions.md).

# 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:

```scheme
(ns reifyhealth.specmonstah-tutorial.08
  (:require [reifyhealth.specmonstah.core :as sm]))

(def schema
  {:user  {:prefix :u}
   :topic {:prefix    :t
           :relations {:owner-id [:user :id]}}
   :post  {:prefix    :p
           :relations {:topic-id [:topic :id]}}})

(defn announce
  [db {:keys [ent-name]}]
  (str "announcing... " ent-name "!"))

(defn ex-01
  []
  (-> (sm/add-ents {:schema schema} {:post [[1]]})
      (sm/visit-ents :announce announce)
      (get-in [:data :attrs])))

(ex-01)
;; =>
{:post  {:type :ent-type},
 :p0    {:type                 :ent,
         :index                0,
         :ent-type             :post,
         :query-term           [1],
         :loom.attr/edge-attrs {:t0 {:relation-attrs #{:topic-id}}},
         :announce             "announcing... :p0!"},
 :topic {:type :ent-type},
 :t0    {:type                 :ent,
         :index                0,
         :ent-type             :topic,
         :query-term           [:_],
         :loom.attr/edge-attrs {:u0 {:relation-attrs #{:owner-id}}},
         :announce             "announcing... :t0!"},
 :user  {:type :ent-type},
 :u0    {:type       :ent,
         :index      0,
         :ent-type   :user,
         :query-term [:_],
         :announce   "announcing... :u0!"}}
```

`(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 {:schema schema} {:todo [[1]]})
    (sm/visit-ents :announce announce)
    (get-in [:data :attrs]))
```

`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`:

1. the ent db
2. 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?
