std.concurrent.request-command
declarative command templates for request clients
Describe command construction, option selection, formatting, processing, and execution mode in a reusable value.
1 Overview
A Command separates public arguments from the command sent to a client. Input formatting runs before request execution; output formatting and process chains run afterward.
2 Walkthrough
2.1 Define a command
(require '[std.concurrent.request-command :as command])
(def sum-command
(command/req:command
{:type :single
:name :sum
:arguments [:numbers]
:function (fn [args _opts] args)
:options {:default {} :select []}
:format {}
:process {}}))
(command/req:run sum-command + [1 2 3])
2.2 Choose an execution mode
Use :bulk or :transact when the command function returns multiple requests. The :retry mode can rebuild and submit a command after a selected failure.
(command/req:command
{:type :bulk
:function build-commands
:options {:bulk {:async true}}
:format {:input normalize-input
:output normalize-output}
:process {:chain [summarize]}})
3 Extension point
Implement another run-request method to add an application-specific execution mode while retaining the common formatting and processing pipeline.
4 API
NONE
(impl/defimpl Command [type name arguments function options format process])
link
Command ^
NONE
(impl/defimpl Command [type name arguments function options format process])
link
format-input ^
[{:keys [format options], :as command} input opts]
Added 3.0
helper for formatting command input
v 3.0
(defn format-input
([{:keys [format options] :as command} input opts]
(if-let [input-fn (:input format)]
(input-fn input (select-keys opts (:select options)))
input)))
link
(format-input {:format {:input (fn [input opts] [input opts])} :options {:select [:id]}} :hello {:id 1 :skip true}) => [:hello {:id 1}]
format-output ^
[{:keys [format options], :as command} output opts]
Added 3.0
helper for formatting command input
v 3.0
(defn format-output
([{:keys [format options] :as command} output opts]
(if-let [output-fn (:output format)]
(output-fn output (select-keys opts (:select options)))
output)))
link
(format-output {:format {:output (fn [output opts] [output opts])} :options {:select [:id]}} :hello {:id 1 :skip true}) => [:hello {:id 1}]
NONE
(impl/defimpl Command [type name arguments function options format process])
link
v 3.0
(defn req:command
([m]
(map->Command m)))
link
(req:command {:type :single :name :echo}) => (contains {:type :single :name :echo})
req:run ^
[command client args] [{:keys [options process], :as command} client args opts]
Added 3.0
runs a command
v 3.0
(defn req:run
([command client args]
(req:run command client args {}))
([{:keys [options process] :as command} client args opts]
(let [input-fn (or (:input options) f/NIL)
output-fn (or (:output options) f/NIL)
input (format-input command args (merge (input-fn args) opts))
out-fn #(format-output command % (merge (output-fn args) opts))
opts (-> (merge (:default options) opts)
(r/req:opts process)
(r/req:opts {:post [out-fn]}))]
(run-request command client input opts))))
link
(with-redefs [run-request (fn [_ client input opts] [client input opts])] (let [[client input opts] (req:run {:options {:select [:id] :input (fn [args] {:id (:id args)}) :output (fn [_] {:id 2})} :format {:input (fn [input opts] [input opts]) :output (fn [output opts] [output opts])} :process {:chain [inc]}} :client {:id 1 :value 2} {:extra true})] [client input (map? opts) (= [inc] (:chain opts)) (= 1 (count (:post opts))) (fn? (first (:post opts)))])) => [:client [{:id 1, :value 2} {:id 1}] true true true true]
v 3.0
(defmulti run-request
(fn [{:keys [type]} client args opts] type))
link
(with-redefs [std.concurrent.request/req-fn (fn [client command opts] [client command opts])] (let [[client command opts] (run-request {:type :single :function (fn [args _] {:command args})} :client [:hello] {:async true})] [client command (:async opts) (contains? opts :context)])) => [:client {:command [:hello]} true true]