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



->Command ^

[type name arguments function options format process]

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}]

map->Command ^

[m__7997__auto__]

NONE
(impl/defimpl Command [type name arguments function options format process])
link

req:command ^

[m]
Added 3.0

constructs a command

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]

run-request ^

Added 3.0

extensible function for command templates

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]