-
Notifications
You must be signed in to change notification settings - Fork 0
Simulating Service Push
To follow along with this section, start with tag v2.0.3.
The application now has a simple counter which can be incremented by some action in the user interface. The plan for this tutorial is to create an application where each user connected to a service will see the counters of all of the other connected users. This means that the application will need to send its counter value to a service and receive other counter values.
Instead of building the service and working on actually transmitting messages over a network, this section will show you how to build a simple service simulator. This will allow you to continue work on the client side of the application and delay work on the service until it is actually required.
Pedestal-app makes it easy to work on different parts of an application in isolation. This will allow you to clearly define the data which must be provided to the client in order to support the functions of the user interface.
In this section you will build the service simulator and create a way to store these "other counter" values. While doing this, you will be introduced the following concepts:
- How pedestal-app applications communicate with the outside world
- Simulating services
As usual, start the development tools. You should always have them running. This will be your last reminder.
The application's behavior must be updated to support receiving and
storing other counter values. To do this, you will add a new transform
function. This transform will be named swap-transform and will
simply get the value associated with the :value key in the message
and return that as the new value in the data model. It can be used any
time you would like to replace a value in our data model.
(defn swap-transform [_ message]
(:value message))Add a new entry in the transform vector of our dataflow definition
which will respond to any message with type :swap and any path. As
you continue building this application, :swap will be used often to
updated values.
(def example-app
{:version 2
:transform [[:inc [:my-counter] inc-transform]
[:swap [:**] swap-transform]]
:emit [{:init init-main}
[#{[:*]} (app/default-emitter [:main])]]})To add other counter values you will send a message like the one shown below.
{msg/type :swap msg/topic [:my-counter "abc"] :value 42}In this message "abc" represents the other user's id or name.
Create a new ClojureScript file
tutorial-client/app/src/tutorial_client/simulated/services.cljs for
the simulated service. This implementation will need to occasionally
send a message to the application describing the current
state of various counters.
The code for the service is shown below.
(ns tutorial-client.simulated.services
(:require [io.pedestal.app.protocols :as p]
[io.pedestal.app.messages :as msg]
[io.pedestal.app.util.platform :as platform]))
(def counters (atom {"abc" 0 "xyz" 0}))
(defn increment-counter [key t input-queue]
(p/put-message input-queue {msg/type :swap
msg/topic [:other-counters key]
:value (get (swap! counters update-in [key] inc) key)})
(platform/create-timeout t #(increment-counter key t input-queue)))
(defn receive-messages [input-queue]
(increment-counter "abc" 2000 input-queue)
(increment-counter "xyz" 5000 input-queue))
(defrecord MockServices [app]
p/Activity
(start [this]
(receive-messages (:input app)))
(stop [this]))This code defines a service (MockService) which, when started, will send
messages to the application (via receive-messages) which report updates to
the two counters. One counter is updated every two seconds and the other every
five seconds. When MockServices is created it must be passed the application
to which it will send messages. There are two interesting new things here: the
io.pedestal.app.util.platform namespace and the Activity protocol.
The Activity protocol is used for things which can start and stop. This
mock service and the real service will implement the same protocol so
downstream code does not have to know which implementation is being used.
The io.pedestal.app.util.platform namespace contains functions which
require platform-specific implementations. It is used in the example
above to create a timeout. Creating a timeout in this way will work in
both Clojure and ClojureScript code. Because this is a ClojureScript
file, you could have used JavaScript interop here and called
js/setTimeout instead.
The main function in tutorial-client.simulated.start is where the
application begins execution when you are viewing the application in
the Data UI aspect (this is configured in
tutorial-client/config/config.clj). This is where the simulated
service should be started.
First, require some additional namespaces
[io.pedestal.app.protocols :as p]
[tutorial-client.simulated.services :as services]and then we create and start MockServices.
(defn ^:export main []
(let [app (start/create-app d/data-renderer-config)
services (services/->MockServices (:app app))]
(p/start services)
app))Up to this point, the emitter you have been using is reporting all changes. Replace that emitter with the one shown below.
[#{[:my-counter] [:other-counters :*]} (app/default-emitter [:main])]This emitter configuration will only show updates to [:my-counter]
and [:other-counters :*] (any of the other counters). By setting up
the emitters in this way, it will be easy to turn on and off individual
emitters during development.
After all of the changes above, the dataflow definition should now look like the one shown below.
(def example-app
{:version 2
:transform [[:inc [:my-counter] inc-transform]
[:swap [:**] swap-transform]]
:emit [{:init init-main}
[#{[:my-counter] [:other-counters :*]} (app/default-emitter [:main])]]})Back in the browser, if you refresh the Data UI, you will see counters for two other players which will be updated every few seconds.
Using a simulator in this way allows you to see how the application will work when it is being used by multiple users. This kind of thing is hard and time consuming to simulate manually. It also allows you to quickly change things on the client when you need to, waiting to change back-end services until client needs have stabilized.
This is only one half of the simulator. In the next section we will simulate publishing the local counter to other users.
The tag for this section is v2.0.4.