A Clojure library designed to make writing workflows simple.
workflows
artifacts are released to Clojars.
If you are using Maven, add the following repository definition to your pom.xml
:
<repository>
<id>clojars.org</id>
<url>http://clojars.org/repo</url>
</repository>
With Leiningen:
[com.andrewmcveigh/workflows "0.2.0"]
The most recent release can be found on Clojars.
The complete API documentation is also available (marginalia generated).
A workflow is a map conforming to the workflows.core/Workflow
schema. The constructor function workflow
can be used to create a
workflow. It doesn't do much, you can create one manually if you like.
Workflows need tasks. Tasks are maps conforming to the
workflows.core/Task
schema. If a task has a :workflows.core/wait
key, it's a waiting task.
(use 'workflows.core)
(workflow (task #(prn 5)) (task #(prn 6) #(prn 7)))
=> {:position 0,
:flow
[{:workflows.core/work #<fn>
{:workflows.core/wait #<fn> :workflows.core/work #<fn>}]}
Workflows are started with #'workflows.core/work
. #'work called with
no args starts a workflow. It's presumed that non-waiting tasks are
no-args functions.
(work (workflow (task #(prn 5)) (task #(prn 6) #(prn 7))))
5
6
=> {:waiting? true,
:position 1,
:flow [{:workflows.core/work #<fn>}
{:workflows.core/work #<fn>, :workflows.core/wait #<fn>}]}
When a :workflows.core/wait
task is run, it pauses the workflow. The
workflow is restarted by calling work on it again.
A :waiting?
workflow is pretty useless if it isn't stored
somewhere. They can be kept pretty easily in a ref, or atom.
(def workflows
(atom
{"id" (workflow (task #(prn 5)) (task #(prn 6) #(prn %)))}))
=> #'user/workflows
Waiting tasks are often waiting for input. #'work called with args supplies this input to the task's work function by applying the args. If you're keeping a workflow in an atom, it can be restarted pretty easily too.
(swap! workflows update-in ["id"] work)
5
6
=> {"id" {:waiting? true,
:position 1,
:flow [{:workflows.core/work #<fn>}
{:workflows.core/work #<fn>, :workflows.core/wait #<fn>}]}}
(swap! workflows update-in ["id"] work "finished!")
"finished!"
=> {"id" {:position 2,
:flow [{:workflows.core/work #<fn>}
{:workflows.core/work #<fn>, :workflows.core/wait #<fn>}]}}
You can check the status of a workflow pretty easily with complete?
or :waiting?
aswell.
Often workflows will need to be saved or transmitted. As workflows are
just maps they can be serialized easily with
clojure.core/pr-str
. Unfortunately though, clojure functions, and
java Objects are not easily serializable.
The workflows.core/task-fn
macro can help with that. It creates a
clojure record that can be invoked like a regular clojure function,
but serializes to its source code when pr-str
is called on it. You
can then clojure.core/load
the source code to recreate the workflow/task.
(w/workflow (w/task (w/task-fn [] (println "Task 1"))))
=> {:position 0,
:flow [{:workflows.core/work
(workflows.core/task-fn [] (clojure.core/println "Task 1"))}]}
(pr-str (w/workflow (w/task (w/task-fn [] (println "Task 1")))))
=> "{:position 0, :flow [{:workflows.core/work (workflows.core/task-fn [] ..."
(load-string (pr-str (w/workflow (w/task (w/task-fn [] (println "Task 1"))))))
=> {:position 0,
:flow [{:workflows.core/work
(workflows.core/task-fn [] (clojure.core/println "Task 1"))}]}
Copyright © 2014 Andrew Mcveigh
Distributed under the Eclipse Public License, the same as Clojure.