Skip to content

Commit 76ee287

Browse files
committed
Fix #770: unwrap var-from-def before applying the viewer
When a `def` has an explicit viewer like `^{::clerk/viewer clerk/row}`, the viewer should see the def's value, not the internal `{:var-from-def ... :var-snapshot ...}` map Clerk adds around it. Before, that map was passed to function viewers, which either crashed (#770) or rendered the raw map. Now the value is unwrapped first, unless the viewer opts out with `:var-from-def? true` (as `render-eval-viewer` does).
1 parent e8c4dfb commit 76ee287

2 files changed

Lines changed: 41 additions & 24 deletions

File tree

src/nextjournal/clerk/viewer.cljc

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -429,27 +429,30 @@
429429
(defn var-from-def? [x]
430430
(var? (get-safe x :nextjournal.clerk/var-from-def)))
431431

432+
(def unwrap-var-value (some-fn :nextjournal.clerk/var-snapshot
433+
(comp deref :nextjournal.clerk/var-from-def)))
434+
432435
(def var-from-def-viewer
433436
{:name `var-from-def-viewer
434437
:pred var-from-def?
435-
:transform-fn (update-val (some-fn :nextjournal.clerk/var-snapshot
436-
(comp deref :nextjournal.clerk/var-from-def)))})
438+
:transform-fn (update-val unwrap-var-value)})
437439

438440
(defn apply-viewer-unwrapping-var-from-def
439441
"Applies the `viewer` (if set) to the given result `result`. In case
440-
the `value` is a `var-from-def?` it will be unwrapped unless the
441-
viewer opts out with a truthy `:nextjournal.clerk/var-from-def`."
442+
the `value` is a `var-from-def?` it will be unwrapped (so that the
443+
viewer operates on the def's value) unless the viewer map opts out
444+
with a truthy `:var-from-def?`."
442445
[{:as result :nextjournal/keys [value viewer]}]
443446
(if viewer
444-
(let [value+viewer (if (or (var? viewer) (fn? viewer))
445-
(viewer value)
446-
{:nextjournal/value value
447-
:nextjournal/viewer (normalize-viewer viewer)})
448-
{unwrap-var :transform-fn var-from-def? :pred} var-from-def-viewer]
449-
(assoc result :nextjournal/value (cond-> value+viewer
450-
(and (var-from-def? value)
451-
(-> value+viewer ->viewer :var-from-def? not))
452-
unwrap-var)))
447+
(let [opts-out? (and (map? viewer) (:var-from-def? viewer))
448+
value' (cond-> value
449+
(and (var-from-def? value) (not opts-out?))
450+
unwrap-var-value)
451+
value+viewer (if (or (var? viewer) (fn? viewer))
452+
(viewer value')
453+
{:nextjournal/value value'
454+
:nextjournal/viewer (normalize-viewer viewer)})]
455+
(assoc result :nextjournal/value value+viewer))
453456
result))
454457

455458
#?(:clj

test/nextjournal/clerk/viewer_test.clj

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -212,19 +212,33 @@
212212
(is (= [{:render-fn 'foo}] (v/get-viewers 'nextjournal.clerk.viewer-test.random-ns-name)))))
213213

214214
(def my-test-var [:h1 "hi"])
215+
(def my-test-var2 1)
216+
217+
(def apply+get-value #(-> % v/apply-viewer-unwrapping-var-from-def :nextjournal/value :nextjournal/value))
215218

216219
(deftest apply-viewer-unwrapping-var-from-def
217-
(let [apply+get-value #(-> % v/apply-viewer-unwrapping-var-from-def :nextjournal/value :nextjournal/value)]
218-
(testing "unwraps var when viewer doens't opt out"
219-
(is (= my-test-var
220-
(apply+get-value {:nextjournal/value [:h1 "hi"] :nextjournal/viewer v/html})
221-
(apply+get-value {:nextjournal/value {:nextjournal.clerk/var-from-def #'my-test-var} :nextjournal/viewer v/html})
222-
(apply+get-value {:nextjournal/value {:nextjournal.clerk/var-from-def #'my-test-var} :nextjournal/viewer v/html-viewer}))))
223-
224-
(testing "leaves var wrapped when viewer opts out"
225-
(is (= {:nextjournal.clerk/var-from-def #'my-test-var}
226-
(apply+get-value {:nextjournal/value {:nextjournal.clerk/var-from-def #'my-test-var}
227-
:nextjournal/viewer (assoc v/html-viewer :var-from-def? true)}))))))
220+
(testing "unwraps var when viewer doens't opt out"
221+
(is (= my-test-var
222+
(apply+get-value {:nextjournal/value [:h1 "hi"] :nextjournal/viewer v/html})
223+
(apply+get-value {:nextjournal/value {:nextjournal.clerk/var-from-def #'my-test-var} :nextjournal/viewer v/html})
224+
(apply+get-value {:nextjournal/value {:nextjournal.clerk/var-from-def #'my-test-var} :nextjournal/viewer v/html-viewer}))))
225+
226+
(testing "leaves var wrapped when viewer opts out"
227+
(is (= {:nextjournal.clerk/var-from-def #'my-test-var}
228+
(apply+get-value {:nextjournal/value {:nextjournal.clerk/var-from-def #'my-test-var}
229+
:nextjournal/viewer (assoc v/html-viewer :var-from-def? true)}))))
230+
231+
(testing "function viewer receives the deref'd value, not the var-from-def wrapper (fixes #770)"
232+
(is (= [my-test-var2]
233+
(apply+get-value {:nextjournal/value {:nextjournal.clerk/var-from-def #'my-test-var2
234+
:nextjournal.clerk/var-snapshot my-test-var2}
235+
:nextjournal/viewer v/row}))))
236+
237+
(testing "function viewer on a seq-valued def spreads across row cells"
238+
(is (= [1 2 3]
239+
(apply+get-value {:nextjournal/value {:nextjournal.clerk/var-from-def #'my-test-var
240+
:nextjournal.clerk/var-snapshot [1 2 3]}
241+
:nextjournal/viewer v/row})))))
228242

229243

230244
(deftest resolve-aliases

0 commit comments

Comments
 (0)