"Function hooks; background task runner and related hooks.
Namespaces under `metabase.task.*` will be automatically loaded when the background task runner is started."
"Background task scheduling via Quartzite. Individual tasks are defined in `metabase.task.*`"
(:requireclojure.java.classpath
[clojure.tools.logging:aslog]
[clojure.tools.namespace.find:asns-find]
[metabase.util:asu])
(:importjava.util.Calendar))
[clojurewerkz.quartzite.scheduler:asqs]))
;; # HOOKS
;; [Just like in Emacs Lisp](http://www.gnu.org/software/emacs/manual/html_node/elisp/Hooks.html) (well, almost) <3
;;
;; Define a hook with `defhook`, add functions to it with `add-hook!`, and run those functions later with `run-hook`:
;;
;; (defhook hourly-tasks-hook \"Tasks to run hourly\") ; define a new hook.
;; (add-hook! #'hourly-tasks-hook my-fn-to-run-hourly) ; add a function to the hook
;; (run-hook #'hourly-tasks-hook :parallel) ; run the functions associated with a hook
;;
;; See the docstrs of these functions below for further discussion.
;;
;; ### Robert Hooke
;; Yes, I known [`robert-hooke`](https://github.com/technomancy/robert-hooke) exists.
;; This library is actually bascially an implementation of Emacs Lisp [`advice`](http://www.gnu.org/software/emacs/manual/html_node/elisp/Advising-Functions.html),
;; not `add-hook`. Which is what we want here. Also, the implementation below is only like ~20 LOC so no need to pull in a 3rd-party library IMO.
;;
;; ### Differences from Emacs Lisp
;;
;; (defun add-hook (hook function &optional append local)
;; ...)
;; (add-hook 'my-wacky-hook #'some-fun t t)
;;
;; 1. In Elisp, calling `add-hook` with an undefined hook will just create the hook for you. We're not allowing that here so we can do
;; some safety checking.
;; 2. You can't define a `buffer-local` hook because there's no such thing in Clojure
;; 3. Excution order of *hook functions* here are indeterminate since they're stored in a set
;; 4. We can run our *hook functions* in parallel <3
;;
(defmacrodefhook
"Define a new hook.
(defhook hourly-tasks-hook \"Tasks to run hourly\")
A hook is simply an atom storing a set of functions that you can run at any time with `run-hook`."
[hook-name&[docstr?]]
{:arglists'([hook-namedocstr?])}
`(defonce~(vary-metahook-nameassoc
:docdocstr?
:type::hook)
(atom#{})))
;; TODO Should we require that F be a var so as to avoid duplicate lambdas being added ?
(defnadd-hook!
"Add function F to HOOK (hereafter, known as one of HOOK's *hook functions*).
Calling `(run-hook #'hook)` at a later time will call this function.