Monthly Archives: February 2011

Designing Yaclot: Generic Clojure Conversion Library

In real Clojure applications you often cannot avoid explicit type conversions, even though the language does not require you to explicitly specify types all the time. Another great feature of Clojure is its universal data structure: most of the time you don’t need anything more than a simple map or record. However, these records often need to be a bit different in different areas of the applications.

Rationale

Examples? You may have a java.sql.Date in database and backend, but your web front end provides you with a String. Or your backend uses rational numbers, but you need to format them into nice Strings for presentation.

Sometimes you operate on whole records. From database you may pull the following:

(def db-sample
  {:date    2011-02-01 ; java.sql.Date
   :balance 2042.00    ; java.math.BigDecimal
   :credit  1000.00    ; java.math.BigDecimal
   :roi     2.13 })    ; java.math.BigDecimal

… for display you would like to use:

(def presentation-sample
  {:date    "Feb 1, 2011" ; String
   :balance "$2,042.00"   ; String
   :credit  "$1,000.00"   ; String
   :roi     "2.13%" })    ; String

… and you need to support input from a web form as:

(def form-sample
  {:date    "2011-02-01"  ; String
   :balance "2042"        ; String
   :credit  "1000"        ; String
   :roi     nil })        ; Rational calculated 
                          ; by back-end from other fields

All three have very similar structure, but implementing all these transformations can be pain.

Planned API for generic converter

I am implementing a library that will do it for you, with API similar to the following:

(def db-fmt
  {:date    (to-type java.sql.Date)
   :balance (to-type java.math.BigDecimal)
   :credit  (to-type java.math.BigDecimal)
   :roi     (to-type java.math.BigDecimal) })

(def presentation-fmt
  {:date    (using-format "MMM d, yyyy" (to-type String))
   :balance (using-format "$%.2f"       (to-type String))
   :credit  (using-format "$%.2f"       (to-type String))
   :roi     (using-format "%.2f%%"      (to-type String)) })

(def form-fmt
  {:date    (using-format "yyyy-M-d" (to-type java.sql.Date))
   :balance                          (to-type java.math.BigDecimal)
   :credit                           (to-type java.math.BigDecimal) })

(map-convert db-sample presentation-fmt)
=> ; (similar to presentation-sample)

(map-convert form-sample db-fmt)
=> ; (similar to db-sample)

Another feature is generic conversion function for individual values:

(convert "2011-02-12" (to-type java.util.Date))
; => #<Date Sat Feb 12 00:00:00 CET 2011>

(convert (java.util.Date. 111 1 12) (to-type String))
; => "2011-02-12"

(convert 42 (to-type String))
; => "42"

(convert "2/12/11" (using-format "M/dd/yy" (to-type java.util.Date)))
; => #<Date Sat Feb 12 00:00:00 CET 2011>

Ideas for the Future

In the future, this library could support pre- and post-conversion validation. For instance, check that balance is not empty and date is in the correct format before conversion, and validate that balance and credit are positive once they are numbers.

Another idea is using it as a base for yet another HTML form manipulation library. I found existing libraries somewhat disappointing as they imposed to many restrictions on me. I would like to have the ability to manipulate and lay out forms as I please, and only use the bits of the library that I need right now.

Current Status

Currently much of the API and features are designed, but only the above presented part is implemented (not even supporting formats for numbers, only for dates). Once I implement conversions between all popular/basic types I will mark it 0.1 and push to Clojars. Error handling and validation are next on the road map.

The code is available at github.

Feel free to share any comments or ideas.

TDD in Clojure: Mocking & Stubbing

A few minutes into my first real TDD trip in Clojure I discovered there is two reasonable ways I can do tests: Mocking or black-box testing. I decided to go for mocking, and so I discovered clojure.contrib.mock. I found the docs fairly confusing, but finally understood it with a little help of this article and research.

Verify Calls to a Function

Assuming we have a function to compute square of a number, we want to write another function for square of sum.

(defn square [x] (* x x))

Our test can look like this:

(ns squirrel.test.core
  (:use [[clojure.test]])
  (:use [[clojure.contrib.mock]]))

(deftest test-square-of-sum
  (expect [square (has-args [3])]
    (square-of-sum 2 1)))

This use of expect asserts that when we execute the inner form (square-of-sum 2 1), it calls square with argument equal 3. However, it does not execute square itself. The only thing that this test checks is whether square got called. In particular, it does not check what (square-of-sum 2 1) returns. We’ll get back to stubbing in a moment.

Stubbing

Let’s modify our test to also assert the final result:

(deftest test-square-of-sum
  (expect [square (has-args [3])]
    (is (= 9 (square-of-sum 2 1))))

When the test runs, it fails because square-of-sum returns nil. The reason is that expect replaces square with a stub which by default doesn’t return anything.

To have stub return a concrete value, we can use the returns function:

(deftest test-square-of-sum
  (expect [square (returns 9 (has-args [3]))]
    (is (= 9 (square-of-sum 2 1))))

Voila. Now the test passes.

To recap, what this two-line test does is:

  • Create a stub for square which returns 9 for argument of 3.
  • Assert this stub is called with argument 3.
  • Assert that square-of-sum calls this stub with argument 3.
  • Assert that square-of-sum eventually returns the correct value.

Quite a lot for such a tiny test.

Expectation Hash

You may be wondering what exactly the second argument in each binding pair is. Clojure docs call it expectation hash.

Each function that operates on an expectation hash, such as has-args or returns, has two overloaded versions. One of them only takes a value or predicate and returns a new expectation hash. Examples include (returns 9) or (has-args [3]).

The other version takes an expectation hash as the second argument. These versions are used to pass the expectation hash through a chain of decorators. Order of decoration does not matter, so (returns 9 (has-args [3])) is effectively the same as (has-args [3] (returns 9)).