Skip to content
Ryan Neufeld edited this page Jul 9, 2013 · 12 revisions

To follow along with this section, start with tag v2.0.11.

The pedestal-app development tools are themselves just a Clojure web application and can easily be modified to meet the specific needs of a project.

The Tools menu contains several links which allow you to view and work with the application in different ways. Each of these "ways" is called an aspect because it allows you to focus on one part of the application in isolation.

  • Data UI - run application logic without a renderer and with a simulated back-end
  • Render - render without running application logic
  • Development - run everything in development mode
  • Production - run everything with advanced compilation

In this section you will see that it is easy to add new custom aspects.

Combining the custom renderer and the simulated back-end

It would good to have a new aspect that combined the simulated back-end with the custom renderer. This would allow you to see and interact with a more realistic version of the application without having to run a service and simulate activity from multiple browsers.

To add an aspect, open the configuration file for the project.

tutorial-client/config/config.clj

This is a Clojure file with a var named configs. Within this configuration map is an :aspects key associated with a map containing each aspect.

The new aspect will have the key :ui and is shown below.

:ui {:uri "/tutorial-client-dev-ui.html"
     :name "UI"
     :order 2
     :out-file "tutorial-client-dev-ui.js"
     :main 'tutorial_client.simulated.start
     :recording? true
     :logging? true
     :output-root :tools-public}

Most of this configuration is straight forward. You give the aspect a unique URI, name and the order in which it will appear in the tools menu. You also provide a name for the JavaScript output file. Special flags can be set to enable recording or logging for each aspect.

As you can see in other aspects, the optimization level for each aspect can be set using the :optimizations key. Each aspect can also have a :compiler-options key with a map as value. The compiler options map will passed along to the ClojureScript compiler.

The :output-root key determines where compiled output will be written on the file system. There are two possibilities: :public and :tools-public. :public will put compiled output into out/public and :tools-public will compile output to out/tools/public. Because this aspect is only used for development and will not be deployed, we compile output to :tools-public.

The main thing that makes each aspect different is the main function which is run to start the application. Each main function will wire things together in a different way. In the config file you will notice that the :data-ui aspect starts from tutorial_client.simulated.start while the :development and :production aspects start from tutorial_client.start. These are namespaces which contain a function named main.

The new :ui aspect will use the same starting point as the :data-ui aspect.

If you were to restart the server, you would see that there is now a new aspect named UI that is exactly the same as the Data UI aspect. They are the same because they have the same starting point. You could make a new starting point but the only difference between these two aspects is the renderer which will be used. It would be better to update the main function in tutorial_client.simulated.start to dynamically select the renderer based an a URL parameter.

To cause an aspect to send a parameter, add a :params key. Add the following key and value to the :data-ui aspect.

:params "renderer=auto"

You can now restart the server to test that the two aspects work and the new parameter is being sent when you use the Data UI aspect.

Determine the renderer to use at runtime

In the namespace tutorial-client.simulated.start, add the following new required namespaces.

[tutorial-client.rendering :as rendering]
[goog.Uri :as guri]

Update the main function to choose the renderer to use based on the value of the renderer parameter in the URL. The updated version of main is shown below.

(defn ^:export main []
  (let [uri (goog.Uri. (.toString (.-location js/document)))
        renderer (.getParameterValue uri "renderer")
        render-config (if (= renderer "auto")
                        d/data-renderer-config
                        (rendering/render-config))
        app (start/create-app render-config)
        services (services/->MockServices (:app app))]
    (app/consume-effects (:app app) services/services-fn)
    (p/start services)
    app))

If you now refresh the browser, you will see that the Data UI aspect uses the data renderer while the UI aspect now uses the custom renderer. Both aspects use the same simulated back-end so you can now see updates to the other counters in the custom renderer.

This technique of selecting a renderer at runtime based on parameters can be very useful. It could be used for A/B testing or for incrementally releasing a new renderer or for selecting completely different renderers based on the device type.

Next steps

You have created a working client. It is now time create a service. In the next section you will build a simple service for the client to talk to. In the section after that you will connect to the server and have a completely working interactive counter application.

If you not interested in creating the service code, checkout tag v2.0.13 and skip to Connecting to the Service.

The tag for this section is v2.0.12.

Home | Making the Service | Connecting to the Service