-
Notifications
You must be signed in to change notification settings - Fork 0
Simulating Effects
To follow along with this section, start with tag v2.0.4.
The application will need to publish the local counter value via the service. In this section you will enhance the service simulator to simulate this behavior. While making this change you will learn about the following pedestal-app concepts:
- Effect functions
- Consuming effects from the effect queue
The application is now receiving input from the outside world. In this section you will let it have an effect on the outside world by allowing it to send its counter value to a service.
So far, this tutorial has introduced two kinds of dataflow functions: transforms and emitters. Transform functions deal with input and emitters deal with output. Emitters take an input map as input and return a vector of rendering deltas. Applications need to produce another kind of output. They need the ability to have an effect on the outside world. The pedestal-app dataflow accomplishes this with the effect function.
An effect function takes its inputs as arguments and returns a vector
of messages which are to be sent out of the application. These
messages can be arbitrary Clojure data and there are no rules about
what can be done with them.
In this section, you will use an effect function to publish the local
counter value to the service.
In the namespace tutorial-client.behavior, create the following
effect function.
(defn publish-counter [count]
[{msg/type :swap msg/topic [:other-counters] :value count}])As with all dataflow functions except for transform functions, the
exact arguments to an effect function will depend on how that
function is configured. The publish-counter function takes the
current count as an argument.
The returned message is similar to the message which we expect to
receive from the service except that the path does not include the id
value. When this message arrives at the service, the session id will
be added to the topic before the message is forwarded to the other
clients.
To add this function to the dataflow, you add an :effect key. Effects
are configured as a set of vectors. Sets are used because order is not
important and there can be no duplicates. Each vector has three elements: a
set of inputs, the effect function and the arguments specification.
:effect #{[#{[:my-counter]} publish-counter :single-val]}The arguments specification indicates that the function should be
passed a single value which will be the value at :my-counter in the
data model. For more information on argument specifiers, see [[Derived
Values]].
This configuration will cause the publish-counter function to be
called whenever the value at :my-counter changes.
The complete dataflow definition will now look like the one shown below.
(def example-app
{:version 2
:transform [[:inc [:my-counter] inc-transform]
[:swap [:**] swap-transform]]
:effect #{[#{[:my-counter]} publish-counter :single-val]}
:emit [{:init init-main}
[#{[:my-counter] [:other-counters :*]} (app/default-emitter [:main])]]})What happens to effects after they are generated? Effect messages
are put on a queue and can be consumed by a service. The simulated
service will handle these messages by printing them to the JavaScript
console.
In the tutorial-client.simulated.services namespace, add the following
function which will be used to process outgoing messages. In the real
service, this function would send the message over the network to a
back-end service.
(defn services-fn [message input-queue]
(.log js/console (str "Sending message to server: " message)))The handler function receives the message and the input-queue. The
input-queue is used to allow callbacks to send results back to the
application. Here it is ignored.
The main function in the namespace tutorial-client.simulated.start
is where the simulated version of the application starts. This is
where you will arrange for some function to receive each message that
is placed on the effects queue.
First, require the io.pedestal.app namespace.
[io.pedestal.app :as app]Next, use the consume-effects function to cause
services/services-fn to receive each message from this queue.
(app/consume-effects (:app app) services/services-fn)The main function should now look like the one shown below.
(defn ^:export main []
(let [app (start/create-app d/data-renderer-config)
services (services/->MockServices (:app app))]
(app/consume-effects (:app app) services/services-fn)
(p/start services)
app))To test that this works, refresh the Data UI page, open the JavaScript console and click on the Increment Counter button. You should see the message printed in the console.
This simple application can now store a few basic values in its data
model and can send and receive messages. The next section will
introduce dataflow in a bit more detail as it describes how to use
derive functions to generate new values.
The tag for this section is v2.0.5.