Tutorial 17 - REPLing with Enlive
March 7, 2017 · View on GitHub
In the previous tutorial, we reached an important milestone on our path of reducing code duplication. We were able to share the form validation and unit testing code between both the client and server. Anytime in the future that we need to update the validation rules and the corresponding unit testing, we'll be able to do it in one shared location, which is a big plus in terms of maintenance time and costs.
Preamble
To start working from the end of the previous tutorial, assuming you have git installed, do as follows:
git clone https://github.com/magomimmo/modern-cljs.git
cd modern-cljs
git checkout se-tutorial-16
Introduction
In this tutorial we're going to integrate the validators for the Shopping Calculator with the corresponding WUI in such a way that the user will receive an error message if they enter an invalid value in the web form.
We have two options. The first is to be rigid about the progressive enhancement strategy, and begin by adding the validators to the server-side code. Or, we can be more flexible and begin by adding the validators to the client-side code. Although this series of tutorials is mainly dedicated to CLJS, we're going to start from the CLJ code first, and forget about the CLJS code for a while.
We have already used Enlive in
Tutorial 13 - Better Safe Than Sorry (Part 1) to implement the
server-side only Shopping Calculator. While we may have been a bit
terse in explaining the details of Enlive, we were able to
directly connect the /shopping action to the shoppingForm
submission by exploiting the fact that the deftemplate macro
implicitly defined a function with the same name as the defining
template.
Let's look at src/clj/modern_cljs/core.clj and src/clj/modern_cljs/templates/shopping.clj.
(defroutes handler
(GET "/" [] "Hello from Compojure!") ; for testing only
(files "/" {:root "target"}) ; to serve static resources
(POST "/login" [email password] (authenticate-user email password))
(POST "/shopping" [quantity price tax discount]
(shopping quantity price tax discount))
(resources "/" {:root "target"}) ; to serve anything else
(not-found "Page Not Found"))
(deftemplate shopping "shopping.html"
[quantity price tax discount]
[:#quantity] (set-attr :value quantity)
[:#price] (set-attr :value price)
[:#tax] (set-attr :value tax)
[:#discount] (set-attr :value discount)
[:#total] (set-attr :value (format "%.2f" (calculate quantity price tax discount))))
The /shopping URI is linked to the shopping function.
deftemplate implicitly defines the shopping function.
However, as we saw in
Tutorial 14 - Better safe than sorry (Part 2), we can easily
break the shoppingForm simply by entering a non-numeric value,
because the calculate function is only able to deal with
stringified numbers.

To begin fixing this bug, we introduced form validators and we made
them portable from CLJ to CLJS by simply using the
Reader Conditionals
introduced by the 1.7.0 release if CLJ/CLJS.
With the intent of covering many possible usages of the Shopping Form, we introduced unit testing and, thanks again to Reader Conditionals, we made them portable as well.
Code Refactoring again
All of this work isn't very useful if the form doesn't make use of the form validators we have just constructed! To reach this goal, we need to refactor the code again.
Step One - The middle man
Instead of directly associating the POST "/shopping request with the
corresponding shopping Enlive template, we are going to add
a new function to invoke the validators.
Start the TDD environment
As usual, we want to work in a live environment. This time we can even
exploit the result of the previous tutorial by launching the
tdd task which offers an auto-running test suite for both CLJ and
CLJS as well.
cd /path/to/modern-cljs
boot tdd
...
Elapsed time: 26.573 sec
Now launch the client REPL as usual
# from a new terminal
cd /path/to/modern-cljs
boot repl -c
...
boot.user=>
Disable JavaScript and visit the Shopping Calculator URI.
Open the shopping.clj source file from the
src/clj/modern_cljs/templates directory and modify it as follows.
(ns modern-cljs.templates.shopping
(:require [net.cgrand.enlive-html :refer [deftemplate set-attr]]
[modern-cljs.remotes :refer [calculate]]
;; added the requirement for the form validators
[modern-cljs.shopping.validators :refer [validate-shopping-form]]))
;; template renamed
(deftemplate update-shopping-form "shopping.html"
[quantity price tax discount errors] ; added errors argument
[:#quantity] (set-attr :value quantity)
[:#price] (set-attr :value price)
[:#tax] (set-attr :value tax)
[:#discount] (set-attr :value discount)
[:#total] (set-attr :value
(format "%.2f" (double (calculate quantity price tax discount)))))
;; new intermediate function
(defn shopping [q p t d]
(update-shopping-form q p t d (validate-shopping-form q p t d)))
NOTE 1: By defining the new intermediate function with the same name (i.e.
shopping) previously associate with thePOST "/shopping"request, we do not need to modify thedefroutesmacro call in themodern-cljs.corenamespace. Obviously we had to rename the Enlive template too.
The first code refactoring step needed for injecting the validators in the form has been very easy.
As soon as you save the file, the TDD environment re-executes the validators tests for both CLJ and CLJS.
Don't panic with Enlive
We now need to manipulate the HTML source to inject the eventual error/help message in the right place for each invalid input value typed in by the user.
For example, if the user typed in "foo" as the value of the
price input field we'd like to show to her/him the following
notification.

The original HTML fragment
<div>
<label for="price">Price Per Unit</label>
<input type="text"
name="price"
id="price"
value="1.00"
required>
</div>
has to be transformed in the following HTML fragment
<div>
<label class="help" for="price">Price has to be a number</label>
<input type="text"
name="price"
id="price"
value="foo"
required>
As we quickly learned in a previous tutorial, Enlive is very powerful. By adopting a superset of CSS-like selectors and predefining a rich set of transformers it should allow us to make the needed HTML transformation.
That said, at the beginning Enlive is not so easy to work with, even by following some good tutorials available online. Enlive is full of very clever macros and HOF definitions which constitute a DSL for HTML/XML scraping and templating. You need to spend some time getting familiar with the Enlive lib. Often, the best way to get familiar with a new library in CLJ is by playing with it in the REPL.
REPLing with Hiccup
Before we start REPLing around, do yourself a favor: do your REPLing by using the hiccup lib by James Reeves, because it will avoid the headache of writing stringified HTML at the REPL.
One of the nice features of boot is that it allows you to add
dependencies at runtime, such as when you want to experiment with a
lib and you aren't sure you want to include it in the project file yet.
From the CLJ REPL that we previously launched, let's temporarily add
hiccup as a dependency, and then require the needed namespace:
boot.user=> (set-env! :dependencies #(conj % '[hiccup "1.0.5"]))
nil
boot.user=> (require '[hiccup.core :refer [html]])
nil
We are now ready to start REPLing with hiccup.
Hiccup is a very simple library to use. It lets us emit stringified HTML code from CLJ data structures. It uses vectors to represent HTML elements, and maps to represent the elements' attributes.
For example, if we want to create an HTML fragment for the price
input field when the user typed in an invalid value (e.g. foo), we
could issue the following Hiccup form
boot.user> (html [:div
[:label.help {:for "price"} "Price has to be a number"]
[:input#price {:name "price" :min "1" :value "foo" :required "true"} ]] )
"<div>
<label class=\"help\" for=\"price\">Price has to be a number</label>
<input id=\"price\" min=\"1\" name=\"price\" required=\"true\" value=\"foo\" />
</div>"
NOTE 2: As you can see, Hiccup understands CSS-style shortcuts for denoting the
idandclassattributes (e.g.:input#priceand:label.help, respectively).
REPLing with Enlive
Christophe Grand, the author of Enlive, was aware of the
need to experiment with his powerful and complex DSL in the REPL and
kindly defined a sniptest macro just for that. The sniptest
receives a stringified HTML as a first argument and optionally one or
more pairs of selectors/transformations. This allows it to mimic the
deftemplate macro behaviour in the REPL.
In the active REPL, require the Enlive namaspace as follows,
boot.user> (require '[net.cgrand.enlive-html :as e] )
nil
and call the sniptest macro by passing it, a single argument, the
result of the Hiccup html function, which emits a string of HTML.
boot.user> (e/sniptest (html [:div [:label {:for "price"} "Price"]]))
"<div>
<label for=\"price\">Price</label>
</div>"
Here we used the sniptest macro without any selector/transformation
form, and it just returned the passed argument (i.e. the stringified
HTML fragment built by the Hiccup html function)
Let's add a selector/transformation pair for selecting the label
and changing its content from Price per Unit to Price has to be a number.
boot.user> (e/sniptest (html [:div [:label {:for "price"} "Price per Unit"]] )
[:label] (e/content "Price has to be a number"))
"<div>
<label for=\"price\">Price has to be a number</label>
</div>"
We obtained what we were expecting. So far so good. But what if there
are, as in our shopping.html source, more label elements
contained in the fieldset element? Let's REPL this scenario.
boot.user> (e/sniptest (html [:fieldset [:div [:label {:for "price"} "Price per Unit"]]
[:div [:label {:for "tax"} "Tax (%)"]]] )
[:label] (e/content "Price has to be a number"))
"<fieldset>
<div>
<label for=\"price\">Price has to be a number</label>
</div>
<div>
<label for=\"tax\">Price has to be a number</label>
</div>
</fieldset>"
The [:label] selector selected both label elements inside the
fieldset element. The corresponding transformer consequently
changed the contents of both of them. Luckly, Enlive offers a rich set
of predicates which can be applied to be more specific within the
selectors. One of them is attr=, which tests if an attribute has a
specified value. Let's see how it works in our scenarion.
boot.user> (e/sniptest (html [:fieldset [:div [:label {:for "price"} "Price per Unit"]]
[:div [:label {:for "tax"} "Tax (%)"]]] )
[:label (e/attr= :for "price")] (e/content "Price has to be a number"))
"<fieldset>
<div>
<label for=\"price\">Price per Unit</label>
</div>
<div>
<label for=\"tax\">Tax (%)</label>
</div>
</fieldset>"
Ops, it did not work. What happened?
Hierarchical and conjunction rules
This unexpected behaiour has to do with the Enlive DSL grammar's
rules. The syntax of the [:label (e/attr= :for "price")] selector
says to select any element with a for attribute with the value "price"
contained in a label element (i.e. hierarchical rule). In our
scenario there were no other elements contained inside any label
element, so the selector did not select any node and the transformer
did nothing.
On the other hand, the syntax of the [[:label (e/attr= :for "price")]]
selector is going to select any label which has a for attribute
with the value "price" (i.e. conjunction rule) and this is what we
want. So, to activate the conjunction rule, we need to put the whole
selector in a nested vector. Let's see if it works.
boot.user> (e/sniptest (html [:fieldset [:div [:label {:for "price"} "Price per Unit"]]
[:div [:label {:for "tax"} "Tax (%)"]]])
[[:label (e/attr= :for "price")]] (e/content "Price has to be a number"))
"<fieldset>
<div>
<label for=\"price\">Price has to be a number</label>
</div>
<div>
<label for=\"tax\">Tax (%)</label>
</div>
</fieldset>"
Wahoo! It worked and we're now ready to apply what we've learned by
REPLing with the sniptest macro.
NOTE 3: Enlive selector syntax offers a disjunction rule too, but we're not using it in this tutorial. This rule uses the set syntax
#{ [selector 1] [selector 2] ... [selector n] }to indicate disjunction between selectors.
Select and transform
Let's resume the shopping.clj update-shopping-form template definition we wrote
in the first refactoring step.
(deftemplate update-shopping-form "shopping.html"
[quantity price tax discount errors]
[:#quantity] (set-attr :value quantity)
[:#price] (set-attr :value price)
[:#tax] (set-attr :value tax)
[:#discount] (set-attr :value discount)
[:#total] (set-attr :value (format "%.2f" (double (calculate quantity price tax discount)))))
Here we defined five pairs of selectors/transformations, one for each
input field of the form. All but the final transformer just sets the
corresponding input field value to the value typed in by the user. The
:#total field, however, is set to the value returned by
the calculate function, which will throw an Exception if
it receives an invalid input value (i.e. not a stringified number).
Let's change the last selector/transformation pair to call the
calculate function only when there are no validation errors
(i.e. when validate-shopping-form returns nil).
(deftemplate update-shopping-form "shopping.html"
[quantity price tax discount errors]
[:#quantity] (set-attr :value quantity)
[:#price] (set-attr :value price)
[:#tax] (set-attr :value tax)
[:#discount] (set-attr :value discount)
[:#total] (if errors
(set-attr :value "0.00")
(set-attr :value (format "%.2f" (double (calculate quantity price tax discount))))))
Now we need to substitute the contents of each label relating each
input field with the corresponding error message when its value is
invalid. As we learned from the previous REPL session with the
sniptest macro, to select a single label content we can use the
[[:label (attr= :for <input-name>)]] selector. But what about the
corresponding transformer? We want to transform the content of the
label and set its class to "help" only when the related
input field value is invalid, that is when there are error messages
for it.
(deftemplate update-shopping-form "shopping.html"
[quantity price tax discount errors]
...
...
[[:label (attr= :for "quantity")]] (if-let [err (first (:quantity errors))]
(do-> (add-class "help")
(content err))
identity)
...
...
)
Let's analyze the above code. We already discussed the
[[:label (attr= :for <input-name>)]] selector. The corresponding
trasformer says:
IF there is an error message pertaining the value for the
quantityinput field (i.e.(first (:quantity errors))), THEN add the"help"class to thelabelelement and set the help message as thecontentof thelabel, ELSE do nothing (i.e.identity).
As you can see we are using few more Enlive symbols:
do->: It often happens that you need to apply multiple transformations to the same selected HTML node. Thedo->function chains (i.e. composes) transformations sequentially from left to right (i.e. top to bottom);add-class: lets you add one or more CSS classes to a selected HTML node;content: replaces the content of a selected HTML node with the passed one.
Note that when the value for the input field is valid, we use the CLJ
identity predefined function to leave the content of the element as
it was.
Syntactic sugar
The above transformer would be very boring if we repeated it for each label
of the shoppingForm input fields. Since we're coding
in a LISP, we can express the above
transformation with the simple maybe-error macro, which
receives an expression and expands into the above convoluted code.
(defmacro maybe-error [expr]
`(if-let [x# ~expr]
(do-> (add-class "help")
(content x#))
identity))
Step 2 - Ready to go
We're now ready to finish our deftemplate definition. Following is
the entire content of the shopping.clj source file.
(ns modern-cljs.templates.shopping
(:require [net.cgrand.enlive-html :refer [deftemplate content do-> add-class set-attr attr=]]
[modern-cljs.remotes :refer [calculate]]
[modern-cljs.shopping.validators :refer [validate-shopping-form]] ))
(defmacro maybe-error [expr]
`(if-let [x# ~expr]
(do-> (add-class "help")
(content x#))
identity))
(deftemplate update-shopping-form "shopping.html"
[q p t d errors]
; select and transform input label
[[:label (attr= :for "quantity")]] (maybe-error (first (:quantity errors)))
[[:label (attr= :for "price")]] (maybe-error (first (:price errors)))
[[:label (attr= :for "tax")]] (maybe-error (first (:tax errors)))
[[:label (attr= :for "discount")]] (maybe-error (first (:discount errors)))
; select and transform input value
[:#quantity] (set-attr :value q)
[:#price] (set-attr :value p)
[:#tax] (set-attr :value t)
[:#discount] (set-attr :value d)
; select and transform total
[:#total] (if errors
(set-attr :value "0.00")
(set-attr :value (format "%.2f" (double (calculate q p t d))))))
(defn shopping [q p t d]
(update-shopping-form q p t d (validate-shopping-form q p t d)))
Play and Pray
We're now ready to find out if our long refactoring session has worked.
First, disable the JavaScript engine of your browser. Then,
visit the Shopping Calculator
URI, fill in the form with valid values, and click the Calculate
button. Everything should work as expected.
Let's now see what happens if you type any invalid value into the form,
for example 1.2 as the value for the Quantity input field, foo
as the value for Price, bar as the value for Tax and finally
nothing as the value for Discount.

You should receive the following feedback

It worked as expected. We fixed the server-side code by refactoring it
to inject the form validators into the Enlive template definition for
the shopping.html page. And we also learned a little bit more about
the Enlive DSL.
Stop the CLJ REPL and the boot processes and reset the branch as
usual:
git reset --hard
Next Step - Tutorial 18 - Augmented TDD Session
In the next tutorial we're going to complete the client-side form validation by exploiting the TDD environment augmented with CLJ/CLJS REPLs.
License
Copyright © Mimmo Cosenza, 2012-16. Released under the Eclipse Public License, the same as Clojure.