|
(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 |