dm3/clojure.java-time

Things like `java-time[.api]/truncate-to` do not work correctly when underlying protocol is extended

camsaul opened this issue · 5 comments

In Metabase we extended the Truncatable protocol with some tweaks that we needed to handle certain situations:

https://github.com/metabase/metabase/blob/d0be9fc4c291438204e792f3981bf7fe5a42878a/src/metabase/util/date_2.clj#L289-L298

Previously, we were using a fork of 0.3.3, where the java-time namespace used Potemkin import-vars, which meant that java-time/truncate-to would be updated when the underlying java-time.core/truncate-to var changed (when using extend/extend-protocol/etc.

The newer version of this namespace just uses def to capture the value of java-time.core/truncate-to when the namespace is loaded; any subsequent changes to the underlying var no longer cause java-time/truncate-to or java-time.api/truncate-to to be updated.

I spent a little while trying to debug

java.lang.IllegalArgumentException: No implementation of method: :truncate-to of protocol: #'java-time.core/Truncatable found for class: java.time.LocalDate

errors in Metabase after upgrading to the latest version of this library until I figured out what was going on.

Switching back to Potemkin would prevent others from running in to issues like these. Alternatively, instead of something like

(ns java-time.api ...)

(def truncate-to java-time.core/truncate-to)

maybe you could do

(def truncate-to #'java-time.core/truncate-to)

or even

(defn truncate-to [t unit] (java-time.core/truncate-to t unit))

so the var gets resolved each time you use it.

Thanks for the detailed report. I'll think it over.

Notes:

@camsaul tangentially related, do you compile your app with direct linking?

Fixed in 1.2.0.

@frenchy64, no we don't use direct linking

@camsaul thanks. I since learnt that protocols methods are never directly linked.