Skip to content

Instantly share code, notes, and snippets.

@niclasnilsson
Last active October 8, 2022 16:09
Show Gist options
  • Save niclasnilsson/ae761af9d2b84877b90a58a38349f702 to your computer and use it in GitHub Desktop.
Save niclasnilsson/ae761af9d2b84877b90a58a38349f702 to your computer and use it in GitHub Desktop.
clojure nav
(ns nav-example
"An example of how clojure.datafy/nav can be used."
(:require
[clojure.core.protocols :as p]
[clojure.datafy :as d]
[clojure.set :as set]))
(defn submap?
"Checks whether a is a submap of b"
[a b]
(set/subset? (set a) (set b)))
(defn vary-nav-meta
"assoc clojure.core.protocols/nav to f in metadata of v"
[v f]
(vary-meta v assoc `p/nav f))
(defn create-nav
"creates a nav function that knows how to navigate from a fk to a pk in the
chinook-like data structure"
[fk-map top-coll]
(fn [coll k v]
(let [v (if-let [pk (get fk-map k)]
;; This was a fk, let's find what it points to
(let [nspace (keyword (namespace pk)) ; find the top level key
next-coll (get top-coll nspace) ; get the entries
m {pk v}] ; what the pk entry looks like
(first (filter #(submap? m %) next-coll))) ; find the matching element
;; Normal case, not an fk
v)]
;; Add the nav metadata and return
(vary-nav-meta v (get (meta coll) `p/nav)))))
(defn init-nav [data nav-fn]
(vary-nav-meta data (nav-fn data)))
(def chinook-data
"Data example, extracted from the chinook database and tranformed slightly.
key namespaces and top level key ('table') are the same (:album, :artist, ...).
https://github.com/lerocha/chinook-database"
{:album
[{:album/id 1 :album/title "For Those About To Rock We Salute You" :album/artist-id 1}
{:album/id 2 :album/title "Balls to the Wall" :album/artist-id 2}
{:album/id 3 :album/title "Restless and Wild" :album/artist-id 2}
{:album/id 4 :album/title "Let There Be Rock" :album/artist-id 1}]
:artist
[{:artist/id 1 :artist/name "AC/DC"}
{:artist/id 2 :artist/name "Accept"}]})
(def fk-map
"fk -> pk"
{:album/artist-id :artist/id})
;; Example of how to nav. When we reach a reference (a fk), nav jumps
;; to the entry if refers to.
;;
;; returns {:artist/id 1 :artist/name "AC/DC"}
(as->
(init-nav chinook-data (partial create-nav fk-map)) $
(d/nav $ :album (get $ :album)) ; go to the albums
(d/nav $ 0 (get $ 0)) ; go to album 0
(d/nav $ :album/artist-id (get $ :album/artist-id))) ; nav to the artist who made the album
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment