Skip to content

visiting functions

Daniel Higginbotham edited this page Oct 28, 2022 · 1 revision

datapotato’s written so that you can be productive with it as a test fixture helper out of the box. At its core, however, it’s actually a library for working with entity graphs. It allows you to define schemas and write queries to generate graphs, and it also gives you tools for visiting ent nodes in the graph, executing a function for each node and associating the results with the node.

Visiting ent nodes is kind 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).

The functions you use to generate and insert data, datapotato.core/generate and datapotato.core/insert, are actually just instances of these visiting functions. You could conceivably write your visiting functions to perform some other behavior. I haven’t found a relevant use case yet but the option is there.

This doc explains the ins and outs of visiting functions for people who are curious about how exactly datapotato works or interested in extending it.

Here’s an example:

(ns donut.datapotato-tutorial.visiting-functions
  (:require [donut.datapotato.core :as dc]))

(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
  []
  (-> (dc/add-ents {:schema schema} {:post [{:count 1}]})
      (dc/visit-ents :announce announce)
      (get-in [:data :attrs])))

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

There are a few new things here. First is the call to dc/visit-ents. Visit ents takes three arguments:

  • a potato-db
  • a visit key, :announce in this case
  • a visiting function, in this case announce

NOTE: The name of the visit key (:announce) and the name of the visit function (announce) don’t have to correspond with each other.

The way visiting works is that the visiting function is applied to each ent node in topologically sorted order. In the example above, there’s a :post which refers to a :topic, and a :topic that a :topic that refers to a :user. The visiting function is applied to the :user :u0 first, and the result is stored under the :announce key in :u0’s node attributes.

Node attributes are a feature of the underlying graph representation we’re using, as provided by the library loom. Each node can have a map associated with it. We make use of this to keep track of the results of the announce function, and when generating and inserting data for tests that data is associated with ents as node attributes.

This process is repeated for the other nodes, :t0 and then :p0.

The visiting function announce takes two arguments, a potato-db and visit opts. If you’re curious about what’s included in the visit opts, check out the source code or open a github ticket asking me to explain further - I’ve already spent a lot of time on these docs and don’t know that anyone cares about this particular corner of the implementation 😅

Clone this wiki locally