difform - June 1, 2010

Diffing Clojure Forms

Start with Stuart Sierra's excellent clojure.walk, (to sort the maps/sets,) add Tom Faulhaber's wonderful Clojure pretty print, and finish it off with Neil Fraser's magnificent Java diff library: http://code.google.com/p/google-diff-match-patch/wiki/API, and you'll find it's pretty easy to create a function to diff Clojure forms. Mine's here:

http://github.com/GeorgeJahad/difform

http://clojars.org/difform

Works like this:

(use 'com.georgejahad.difform)
(difform { 1 2 3 4 5 6} { 5 6 1 2 3 7})
   {1 2, 3
 - 4
 + 7
   , 5 6}

As you can see, the two forms differ only in the value of key 3, where it is 4 in the first form and 7 in the second. For a more interesting example,

(difform

{:cart {:items [{:new-delivery {:ids ["-1"]}, :old-unit-price "700.00", :product-id "prod-1", :quantity "1", :unit-price "700.00"}], :old-total-dollars "700.00", :purchase? false, :total-dollars "700.00"}, :day "2010-05-18", :event-ids ["1274166000010:some-session-id-1"], :old-id "14", :delivery {:ids [nil]}, :session "some-session-id-1"}

{:day "2010-05-18", :old-id "14", :session "some-session-id-1", :delivery nil, :cart {:purchase? false, :old-total-dollars "1400.00", :total-dollars "1400.00", :items [{:product-id "prod-1", :old-unit-price "700.00", :unit-price "700.00", :quantity "2", :new-delivery {:ids ["-1"]}}]}, :event-ids ["1274166000000:some-session-id-1"]})

produces the following. Here the values of the quantity, total-dollars, and a few other fields have changed:

   {:cart
    {:items
     [{:new-delivery {:ids ["-1"]},
       :old-unit-price "700.00",
       :product-id "prod-1",
       :quantity "
 - 1
 + 2
   ",
       :unit-price "700.00"}],
     :old-total-dollars "
 - 7
 + 14
   00.00",
     :purchase? false,
     :total-dollars "
 - 7
 + 14
   00.00"},
    :day "2010-05-18",
    :delivery
 - {:ids [
   nil
 - ]}
   ,
    :event-ids ["1274166
 + 0
   0000
 - 1
   0:some-session-id-1"],
    :old-id "14",
    :session "some-session-id-1"}

A very common use is with clojure.test. For example, if clojure test reports you have failed a test like this:

FAIL in (xyz-test) (xyz_spec.clj:7) expected: (= form1 form2)

Run (difform form1 form2) to see what the exact failure was.

Comments/Suggestions

Send any comments/suggestions to George Jahad at "george-clojure at blackbirdsystems.net" or to the main clojure mailing list: http://groups.google.com/group/clojure