Reputation: 457
TL;DR
how to reduce below repeated code, like create two job / trigger from job-inventory
, instead of repeat twice and create terms
;; deps in project.clj
;; [clojurewerkz/quartzite "2.1.0"]
(ns hello.scheduler
(:require [clojurewerkz.quartzite.scheduler :as qs]
[clojurewerkz.quartzite.triggers :as t]
[clojurewerkz.quartzite.jobs :as j]
[clojurewerkz.quartzite.jobs :refer [defjob]]
[clojurewerkz.quartzite.schedule.cron :as cron])
(:use clojure.tools.logging)
(:gen-class))
(def job-inventory
[{:name "add" :task '(+ 1 1) :cron "0/5 * * ? * *"}
{:name "multiply" :task '(* 4 5) :cron "0/3 * * ? * *"}])
(defjob add [ctx] (info "add called, return" (+ 1 1)))
(defjob multiply [ctx] (info "multiply called, return" (* 2 3)))
(defn auto
[]
(let [s (-> (qs/initialize) qs/start)
_ (qs/clear! s)
job (j/build
(j/of-type add)
(j/with-identity (j/key "job.add")))
trigger (t/build
(t/with-identity (t/key "trigger.add"))
(t/start-now)
(t/with-schedule (cron/schedule
(cron/cron-schedule "0/5 * * ? * *"))))
_ (qs/schedule s job trigger)
job (j/build
(j/of-type multiply)
(j/with-identity (j/key "job.multiply")))
trigger (t/build
(t/with-identity (t/key "trigger.multiply"))
(t/start-now)
(t/with-schedule (cron/schedule
(cron/cron-schedule "0/3 * * ? * *"))))
_ (qs/schedule s job trigger)
]
))
similar to what's described in http://clojurequartz.info/articles/getting_started.html , I have block of code to create jobs and hooks them for execution
quesetion is, when I get more and more of them, I wonder if I could have a better way of manage them, like create / spawn from that job-inventory
, instead of actually creating varaibles like add
or multiply
so, asking for one more layers of looping are there ways to utilize function programming, and avoid create new names ( in traditional language says python qt, if I had a sets of button, I could just smash into a giant dictionary, and loop over to create / disable, instead actually create each name as a top level varible )
I tried macro but it says unable to resolve class add, so guess I used it wrong
Upvotes: 1
Views: 170
Reputation: 2280
The key thing to remember is that functions are data. Whilst you can't dynamically create types very easily (as opposed to instances that implement an interface, via reify
), you can statically create a class which then proxies your functions.
First let's make the :task
of the job-inventory
a function.
(def job-inventory
[{:name "add" :task (fn [] (println (+ 1 1))) :cron "0/5 * * ? * *"}
{:name "multiply" :task (fn [] (println (* 4 5))) :cron "0/3 * * ? * *"}])
Then we need the proxy job class. This executes a function it finds within the job data.
;; require clojurewerkz.quartzite.conversion :as qc
(defjob proxy-job [ctx]
(let [ctx (qc/from-job-data ctx)]
((ctx "proxied-fn"))))
Then we create a schedule function which takes a map from the job-inventory and schedules it using the proxy-job to indirect.
(defn schedule [scheduler {:keys [:name :task :cron]}]
(let [job (j/build
(j/of-type proxy-job)
(j/using-job-data {"proxied-fn" task})
(j/with-identity (j/key (str "job." name))))
trigger (t/build
(t/with-identity (t/key (str "trigger." name)))
(t/start-now)
(t/with-schedule (cron/schedule
(cron/cron-schedule "0/3 * * ? * *"))))]
(qs/schedule scheduler job trigger)
scheduler)
(reduce scheduler schedule job-inventory)
It's possible this approach will fall apart if quartz decides to serialise/deserialise the job data. I leave fixing this with another layer of indirection as an easy exercise for the interested reader - to be honest my initial thoughts are that we create named functions which we then refer to by symbol, but it complicates the proxy and makes you wonder why not just use defjob. If you're prepared to defjob your functions, you can still refer to them in a job-inventory and have a data-based job builder function.
Upvotes: 1