Tech behind Tech

Raw information. No finesse :)

Posts Tagged ‘code

Clojure defrecord deftype

with 2 comments


There are different ways to create a struct in clojure.

1) Using Clojure built in data structures

user> (def lotr {:title "Book title"
                 :author {
                          :first-name "first"
                          :last-name "last"}})
#'user/lotr
	

keyword-access

user> (:title lotr)
"Book title"

nested-access

user> (-> lotr :author :first-name)
"first"

2) Using defrecord

user> (defrecord Book [title author])
user.Book
user> (defrecord Person [first-name last-name])
user.Person
user> (def lotr (Book. "Book Title"
                       (Person. "first" "last")))
#'user/lotr

keyword-access

user> (:title lotr)
"Book title"

nested-access

user> (-> lotr :author :first-name)
"first"

As you can see, accessing map and defrecord is very similar. defrecord provides implementation of a persistent map.

3) Using deftype

PS: bad example ( I will explain why later )

user> (deftype Book [title author])
user.Book
user> (deftype Person [first-name last-name])
user.Person
user> (def lotr (Book. "Book Title"
                       (Person. "first" "last")))
#'user/lotr

Unlike defrecord, deftype does not provide implementation of a persistent map. So when you try to keyword-access , it won’t work

keyword-access

user> (:title lotr)
nil

It provides field access only

user> (.title lotr)
"Book Title"

I mentioned the above example for deftype is a bad example. Let me explain why. In OO, we have two set of classes. One set that defines language constructs like String and other that defines domain like Book. As defrecord provides implementation of a persistent map, it is better to use defrecord for realizing domain. Use deftype for realizing programming constructs.

Written by Siva Jagadeesan

November 15, 2010 at 10:58 am

Posted in Clojure

Tagged with , , , ,

Clojure Macros Simplified

with 5 comments


One of the powerful features of clojure ( or any other LISP) is macros. Macros are so powerful that will allow you do things that you can never do in any other language. There is a classic example of implementing “unless” functionality using macros. I am not going to talk about why we need macros and when to use them. There is so much literature out there discussing about Macros. Also checkout Clojure in Action book for in depth look on Macros. I have read both Joy of Clojure and Clojure in Action, when it comes to Macros I will recommend Clojure in Action. The idea behind this blog to simplify macros as much as possible.

As you know in Clojure code is data and data is code. Our code is a clojure List. So we can programmatically create a list and execute it.

user> (def my-code '(println "techbehindtech"))
#'user/my-code
user> my-code
(println "techbehindtech")
user> (eval my-code)
techbehindtech

In above example, we created a list my-code. This list could be created dynamically. And using eval function we could execute that list as a clojure code.

So to create code in clojure, all we have to do is create a clojure list. In clojure, before a code is evaluated we have hook to introduce our own code using Macros system.

In Macros we are basically creating a list of code similar to my-code. Lets create a simple macro that will allow us to write functions with some log message.

(defmacro def-logged-fn [fn-name args & body]
        `(defn ~fn-name ~args
           (println "Calling ...")
           ~@body))
user> (def-logged-fn say[name]
        (println (str "hello " name)))
#'user/say
user> (say "siva")
Calling ...
hello siva
nil

macroexpand and macroexpand-1:

macroexpand and macroexpand-1 are useful functions to know about when using
writing macros. These functions expand our macro forms.

user> (macroexpand-1 '(def-logged-fn say[name]
        (println (str "hello " name))))

(clojure.core/defn
 say
 [name]
 (clojure.core/println "Calling ...")
 (println (str "hello " name)))

You can see that our macro created a fn called “say” that calls println first before the original body. That is pretty cool huh.

The difference between macroexpand-1 and macroexpand is macroexpand-1
will expand only one level of macros and macroexpand calls macroexpand-1
until all macro forms are expanded.

The same macro expanded with macroexpand will look like

(macroexpand '(def-logged-fn say[name]
        (println (str "hello " name))))

(def
 say
 (clojure.core/fn
  ([name]
   (clojure.core/println "Calling ...")
   (println (str "hello " name)))))

As you can see macroexpand expanded defn macro also.

Coming back to our macro, there are 3 reader macros that we have used

- Backtick `
– Tilda ~
– Tilda Ampersand ~@

Backtick ` (syntax-quote):

This is called as syntax-quote. This quotes the whole expression that way you do not have to quote each one of them

Tilda ~ (unquote):

This is called as unquote. This unquotes (substitutes) the value. For
instance, we wanted to replace fn-name with its value, so we tilda
before fn-name.

Tilda Ampersand ~@ (unquote-splicing):

It is easy to show with an example. Lets take our macro that we wrote and change unquote-splicing to unquote before body and let us what happens.

user> (defmacro def-logged-fn [fn-name args & body]
        `(defn ~fn-name ~args
           (println "Calling ...")
           ~body))
#'user/def-logged-fn
user> (macroexpand-1 '(def-logged-fn say[name]
        (println (str "hello " name))))

(clojure.core/defn
 say
 [name]
 (clojure.core/println "Calling ...")
 ((println (str "hello " name))))

You can see the last line looks wierd. It is trying to call output of
println as a function. This will obiviously fail.


user> (def-logged-fn say[name]
         (println (str "hello " name)))
#'user/say
user> (say "techbehindtech")
Calling ...
hello techbehindtech
; Evaluation aborted.

No message.
  [Thrown class java.lang.NullPointerException]

As body is a list ( we are using varible args) when we just unquote it
is putting body inside a single list. To avoid that we have to
unquote-splicing.

Auto-gensym

Inside a macro, sometimes you want to create unqualified symbol to use
in a let block. We can do this easily by appending # in end of symbol.

Written by Siva Jagadeesan

September 28, 2010 at 4:32 pm

Posted in Clojure

Tagged with , , , ,

Compojure Demystified with an example – Part 6

with one comment


In this part we will start implementing edit and delete functionalities.

We are going to build these services,

Edit address – PUT – http://localhost:8080/addresses/:id
Delete address – DELETE – http://localhost:8080/addresses/:id

1) Adding PUT and DELETE route

Edit routes.clj

(ns address_book.routes
  (:use [compojure.core])
  (:require [address-book.address :as address]
            [address-book.middleware :as mdw]
            [compojure.route :as route]
            [clj-json.core :as json]))

(defn json-response [data & [status]]
  {:status (or status 200)
   :headers {"Content-Type" "application/json"}
   :body (json/generate-string data)})

(defroutes handler
  (GET "/addresses" [] (json-response (address/find-all)))
  (GET "/addresses/:id" [id] (json-response (address/find id)))
  (POST "/addresses" {params :params}  (json-response (address/create params)))
  (PUT "/addresses/:id" {params :params} (json-response (address/update (params "id")  params)))
  (DELETE "/addresses/:id" [id] (json-response (address/delete id )))

  (route/files "/" {:root "public"})
  (route/not-found "Page not found"))

(def address-book
     (-> handler
         mdw/wrap-request-logging))

As you can see it is pretty straight forward to add PUT and DELETE route.

What is up with params?

In previous version of Compojure there were some magic variables, like params, session etc. From version 0.4 there are no more magic variables. Instead Compojure provides a map “params”. “params” map will contain all request information. For some reason, params map have a key :params which contains request parameters. That is why we have do {params :params}. I wish they will rename “params” to something else.

Instead of doing {params :params}, the compojure document states that we could do [params id] and when compojure detects a vector, instead of a map it will assign the parameters to each value of the vector. But for some reason, I was not able to make it work.

Front End code

Checkout from github for front end code. Warning: front end code is bad, do not take this as an example.

In next part we will see how to send flash messages.

Source code is now available at github. Created branches for each part.

Next part is posted.

Written by Siva Jagadeesan

September 16, 2010 at 11:09 pm

Posted in Clojure

Tagged with , , , , ,

Compojure Demystified with an example – Part 5

with 2 comments


In this part lets write our own middleware.

From part4 you will remember,

“Middleware are functions that could be chained together to process a request. Middleware functions can take any number of arguments, but the spec stats that first argument should be a handler and function should return a handler. An example for middleware is logging all requests that comes to your webserver.”

1) Create a namespace for middleware

Create src/address_book/middleware.clj

(ns address-book.middleware)

(defn- log [msg & vals]
  (let [line (apply format msg vals)]
    (locking System/out (println line))))

(defn wrap-request-logging [handler]
  (fn [{:keys [request-method uri] :as req}]
    (let [resp (handler req)]
      (log "Processing %s %s" request-method uri)
      resp)))

Our wrap-request-logging is a middleware which takes a handler and returns an handler.

2) Add our middleware to our chain of handlers

Edit routes.clj

(ns address_book.routes
  (:use [compojure.core])
  (:require [address-book.address :as address]
            [address-book.middleware :as mdw]
            [compojure.route :as route]
            [clj-json.core :as json]))

(defn json-response [data & [status]]
  {:status (or status 200)
   :headers {"Content-Type" "application/json"}
   :body (json/generate-string data)})

(defroutes handler
  (GET "/addresses" [] (json-response (address/find-all)))
  (GET "/addresses/:id" [id] (json-response (address/find id)))
  (POST "/addresses" {params :params}  (json-response (address/create params)))

  (route/files "/" {:root "public"})
  (route/not-found "Page not found"))

(def address-book
     (-> handler
         mdw/wrap-request-logging))

3) Test our middleware

Now start your server and you can see all requests are getting logged. This is very powerful feature. We can do security etc using middleware. Ring and Compojure comes with some useful middleware. Check them out.

In next part we will implement edit  and delete functionalities.

Source code is now available at github. Created branches for each part.

Written by Siva Jagadeesan

September 2, 2010 at 12:19 am

Posted in Clojure

Tagged with , , , , ,

Compojure Demystified with an example – Part 4

with 6 comments


In this part we will start implementing Add,  View and View all functionalities.

The services we are going to build are

View All Addresses – GET – http://localhost:8080/addresses
View single address – GET – http://localhost:8080/addresses/:id
Add Address – POST – http://localhost:8080/addresses

Interactive Development using slime

I mentioned in last part that after every change to our code we need to restart our server for our changes to be reflected. This is a pain and against clojure (lisp) philosophy . It would be great if we could eval our modified buffers in emacs and those changes reflected immediately in our jetty server. Thankfully there is very easy way to do this.

In core.clj we are starting jetty adapter. We can start this jetty server in background using future and reload the namespace that we changed. This way we can do interactive development without restarting our server.

PS: Make sure you have emacs setup with slime. I feel emacs is the best IDE for clojure. Again this is my opinion. There are lot of information on how to do this.

I talked about jetty adapter. It is time for us to look at some what little deeper in Compojure.

Compojure

Compojure is based on a library  called Ring. In fact most of clojure web frameworks are based on Ring. So to understand Compojure, it is important to understand Ring.

Ring has three components.

1) Handlers:

Handlers are main functions that  process a request. We define handlers using defroutes macro.

2) Middleware:

Middleware are functions that could be chained together to process a request. Middleware functions can take any number of arguments, but the spec stats that first argument should be an handler and function should return an handler. An example for middleware is logging all requests that comes to your webserver.  Ring and compojure comes with some standard middleware. We will see in next part how to create our own middleware.

3) Adapters:

Adapters are functions could adapt our handler to a web server. We are using jetty adapter to tie our handler to jetty server.

Refactor code to make it easy for interactive development

Edit core.clj

(ns address_book.core
  (:use [compojure.core]
        [ring.adapter.jetty])
  (:require [compojure.route :as route]))

(defroutes example
  (GET "/" [] "<h1>My Address Book!</h1>")
  (route/files "/" {:root "public"})
  (route/not-found "Page not found"))

(future (run-jetty (var address-book) {:port 8080}))

1) Add swank-clojure to our project

swank-clojure comes with lein plugin which will allow us to start a swank server and from emacs you can connect using slime. There are lot of documentation about swank-clojure and slime. Let me know if you need more information. If I see enough interest, I could blog about it or at least point to some good resources.

Edit project.clj

(defproject address_book "1.0.0-SNAPSHOT"
  :description "Address Book"
  :dependencies [[org.clojure/clojure "1.2.0"]
                 [org.clojure/clojure-contrib "1.2.0"]
                 [compojure "0.4.1"]
                 [ring/ring-jetty-adapter "0.2.3"]]
  :dev-dependencies [[swank-clojure "1.2.1"]])

and run

lein deps

Now you should be able to start swank server using

lein swank

2) Break core.clj into different namespaces

Current core.clj is doing two things. Setting up routes and starting jetty server. Lets break it into web_server.clj and routes.clj

rm src/address_book/core.clj

create src/address_book/routes.clj

(ns address_book.routes
  (:use [compojure.core])
  (:require [compojure.route :as route]))

(defroutes address-book
  (GET "/" [] "<h1>My Address Book!</h1>")
  (route/files "/" {:root "public"})
  (route/not-found "Page not found"))

create src/address_book/webserver.clj

(ns address_book.webserver
  (:use [compojure.core]
        [ring.adapter.jetty]
        [address_book.routes :as routes]))

(future (run-jetty (var address-book) {:port 8080}))

3) At last lets create our Address namespace

In this code, I am going to use atoms for persistence. In future I will probably show how we can persist in mysql db using clj-records.

create src/address_book/address.clj

(ns address-book.address
  (:use [address-book.utils number])
  (:refer-clojure :exclude (find create)))

(def STORE (atom {:1 {:id :1 :name "Siva Jagadeesan" :street1 "88 7th" :street2 "#203" :city "Cupertino" :country "USA" :zipcode 98802}}))

(defn create [attrs]
  (let [id (random-number)
        new-attrs (merge {:id id} attrs)]
    (swap! STORE merge {id new-attrs})
    new-attrs))

(defn find-all []
  (vals @STORE))

(defn find [id]
  ((to-keyword id) @STORE))

(defn update [id attrs]
  (let [updated-attrs (merge (find id) attrs)]
    (swap! STORE assoc id updated-attrs)
    updated-attrs))

(defn delete [id]
  (let [old-attrs (find id)]
    (swap! STORE dissoc id)
    old-attrs))

Create utils/number.clj

(ns address-book.utils.number
  (:import [java.util Date]))

(defn to-keyword [num]
  (if-not (keyword? num)
    (keyword (str num))
    num))

(defn random-number []
  (to-keyword (.getTime (Date.))))

PS: I am not writing tests to keep this blog short. But I would advice everyone to write tests.

4)   Lets update routes for add, view and view all functionalities

</span>
<pre>(ns address_book.routes
  (:use [compojure.core])
  (:require [address-book.address :as address]
            [compojure.route :as route]
            [clj-json.core :as json]))

(defn json-response [data & [status]]
  {:status (or status 200)
   :headers {"Content-Type" "application/json"}
   :body (json/generate-string data)})

(defroutes handler
  (GET "/addresses" [] (json-response (address/find-all)))
  (GET "/addresses/:id" [id] (json-response (address/find id)))
  (POST "/addresses" {params :params}  (json-response (address/create params)))

  (route/files "/" {:root "public"})
  (route/not-found "Page not found"))

(def address-book
     handler)

Compojure comes with GET, POST, PUT, DELETE, HEAD and ANY macro to define routes. These macros are self explanatory. Currently we have used GET and POST to define our routes.

Parsing Parameters

Compojure binds request parameters to params.

To get all parameters from request you can destructure map like we did in

POST "/addresses" {params :params}  (json-response (address/create params)))

To get particular parameters from request you can use Compojure sugar syntax like we did in
[sourceode](GET “/addresses/:id” [id] (json-response (address/find id)))[/sourcecode]

5)  Lets build our front end
PS: front end code is a hack. Please don’t follow these codes as a good practice.

Edit public/index.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
        "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>My Address Book</title>
        <link href="/css/address.css" media="screen" rel="stylesheet" type="text/css" />
        <script src="/js/jquery-1.4.2.min.js" type="text/javascript"></script>
        <script src="/js/jquery-ui-1.8.1.custom.min.js" type="text/javascript"></script>
        <script src="/js/jquery.form.js" type="text/javascript"></script>
        <script src="/js/address_book.js" type="text/javascript"></script>
        <script src="/js/address_list.js" type="text/javascript"></script>
</head>
<body>
<div id="wrap">
	<div id="header"><h1>My Address Book</h1></div>

	<div id="main">
          <table id="address">
            <tr><td>Name</td><td id="name"/></tr>
            <tr><td>Street1</td><td id="street1"/></tr>
            <tr><td>Street2</td><td id="street2"/></tr>
            <tr><td>City</td><td id="city"/></tr>
            <tr><td>Country</td><td id="country"/></tr>
            <tr><td>ZipCode</td><td id="zipcode"/></tr>
          </table>
	  <table id="address-list" border="1" width="100%" rules="rows" align="center">
            <tr>
              <th>Name</th>
              <th>Action</th>
            </tr>
            <tr>
              <td >row 1, cell 1</td>
              <td align="center">row 1, cell 2</td>
            </tr>
          </table>
	</div>
	<div id="sidebar">
          <form id="address-form" class="formular" method="post" action="/addresses">
	    <fieldset class="login">
	      <legend>New Address</legend>
	      <div>
		<label for="name">Name</label> <input type="text" id="name" name="name">
	      </div>
              <div>
		<label for="street1">Street1</label> <input type="text" id="street1" name="street1">
	      </div>
               <div>
		<label for="street2">Street2</label> <input type="text" id="street2" name="street2">
	      </div>
              <div>
		<label for="city">City</label> <input type="text" id="city" name="city">
	      </div>
              <div>
		<label for="country">Country</label> <input type="text" id="country" name="country">
	      </div>
               <div>
		<label for="zipcode">ZipCode</label> <input type="text" id="zipcode" name="zipcode">
	      </div>

              <input class="submit" type="submit" value="Create"/>
	    </fieldset>
          </form>
	</div>
	<div id="footer">
	  <p><a href="TechbehindTech.com">TechBehindTech</a> -- Siva Jagadeesan</p>
	</div>
</div>
</body>
</html>

Create public/css/address.css

body,html {
    margin:0;
    padding:0;
    color:#000;
    background:#a7a09a;
}
#wrap {
    width:970px;
    margin:0 auto;
    background: #fffeff;
}
#header {
    padding:5px 10px;
    background: #054477;
	text-align: right;
	color: #fffeff;
}
h1 {
    margin:0;
}
#main {
    float:left;
    width:580px;
    padding:10px;
	background-color: #fffeff;
}
h2 {
    margin:0 0 1em;
	background-color: #fffeff;
}
#sidebar {
    float:right;
    width:350px;
    padding:10px;
    background-color: #fffeff;
}
#footer {
    clear:both;
    padding:5px 10px;
    background: #fed47f;
}
#footer p {
    margin:0;
}
* html #footer {
    height:1px;
}

form * {margin:0;padding:0;} /* Standard margin and padding reset, normally done on the body */

legend {
	color:#000; /* IE styles legends with blue text by default */
	*margin-left:-7px;
	font-weight: bold;
	font-size: 20px;
}
fieldset {
	border:1px solid #dedede; /* Default fieldset borders vary cross browser, so make them the same */
}
fieldset div {
	overflow:hidden; /* Contain the floating elements */
	display:inline-block;
	padding: 10px;
}
fieldset div {display:block;} /* Reset element back to block leaving layout in ie */
label {
	float:left; /* Take out of flow so the input starts at the same height */
	width:8em; /* Set a width so the inputs line up */
}

Create public/js/address_book.js


$(document).ready(function() {
    $("#address").hide();
    $("#address-list").showAddressList();

    $(".address-link").live("click",function(e){
        $.getJSON($(this).attr("href"), function(json) {
            $("#address").showAddress(json);
        });
        e.preventDefault();
    });

    $('#address-form').submit(function(event){
        event.preventDefault();
        var $this = $(this);
        var url = $this.attr('action');
        var dataToSend = $this.serialize();
        var callback = function(data){
            $("#address-list").addAddress(data);
        };
        var options = {
            success:   callback,
            url: url,
            type:     "POST",
            dataType: "json",
            clearForm: true
        };
        $(this).ajaxSubmit(options);
    });

});

Create public/js/address_list.js

function action_links(data){
    var link = "<a href=\"\">edit</a> ";
    link += " | <a href=\"\">delete</a>";
    return "edit | delete";
}

$.fn.showAddressList = function(){
    return this.each(function(){
        var that = this;
        $.getJSON("addresses", function(json) {
            $(that).html(" <tr> <th>Name</th> <th>Action</th> </tr>");
            $.each(json,function(i,data) {
                $(that).append("<tr id=\"address-"+ data.id +" \"><td><a class=\"address-link\" href=\"addresses/" + data.id +"\">" + data.name + "</a></td><td align='center'>" + action_links(data) + "</td></tr>");
            });
        });
    });
};

$.fn.addAddress = function(json){
    var data = json;
    var that = this;
    return this.each(function(){
        var that = this;
        $(that).append("<tr id=\"address-"+ data.id +" \"><td><a class=\"address-link\" href=\"addresses/" + data.id +"\">" + data.name + "</a></td><td align='center'>" + action_links(data) + "</td></tr>");
    });
};

$.fn.showAddress = function(json){
    var data = json;
    var that = this;
    return this.each(function(){
        $(that).slideDown('slow');
        $(that).find("#name").html(data.name);
        $(that).find("#street1").html(data.street1);
        $(that).find("#street2").html(data.street2);
        $(that).find("#city").html(data.city);
        $(that).find("#country").html(data.country);
        $(that).find("#zipcode").html(data.zipcode);
    });
};

Download jquery-1.4.2.min.js, jquery-ui-1.8.1.custom.min.js and jquery.form.js to public/js folder.

6)  Start the web server

[/sourcecode]lein repl src/address_book/webserver.clj [/sourceode]

Wow that was lot of stuff. We will look more into writing our own middlewares in next part.

Source code is now available at github. Created branches for each part.

Part 5 is posted.

Written by Siva Jagadeesan

August 24, 2010 at 4:25 pm

Posted in Clojure

Tagged with , , , , ,

Map, Reduce and Filter in Clojure

with 2 comments


Map

Map applies a function to each element in a collection and returns a new collection with the results of each function.

map.png
user> (map #(+ 10 %1) [ 1 3 5 7 ])
(11 13 15 17)

Reduce

Reduce applies a function on all elements of a collection and returns a value. This value could also be another collection.

reduce.png
user> (reduce * [2 3 4])
24

Filter

Filter applies a predicate function to each element in a collection and returns a new filtered collection.

filter.png
user> (filter even? [1 2 3 4 5 6])
(2 4 6)

Written by Siva Jagadeesan

July 26, 2010 at 10:00 pm

Posted in Clojure

Tagged with , , ,

Parsing XML in Clojure

with 5 comments


Problem :

We need to parse a xml string and be able to query using xpath style tag list.

Ex :

<friends>
  <person>
   <name>Siva</name>
  </person>
</friends>

I need a function that can do this,

(get-value xml :person :name)

returns “Siva”

Solution :

To parse and query xml we need to do these following three things in clojure.

1) Convert xml string (file) to Struct Map

Clojure core comes with a build in xml library (clojure.xml http://clojure.github.com/clojure/clojure.xml-api.html) that has a parse function which takes in InputStream and returns a struct map that represents xml.

(defn get-struct-map [xml]
  (let [stream (ByteArrayInputStream. (.getBytes (.trim xml)))]
    (xml/parse stream)))
user> (get-struct-map xml)
{:tag :friends, :attrs nil, :content [{:tag :person, :attrs nil, :content [{:tag :name, :attrs nil, :content ["Siva"]}]}]}

This struct map is cumbersome to query.

2) Convert Struct Map to Zipper Data Structure

“A zipper is a technique of representing an aggregate data structure so that it is convenient for writing programs that traverse the structure arbitrarily and update its contents, especially in purely functional programming languages.” http://en.wikipedia.org/wiki/Zipper_%28data_structure%29

To make it easy for us to traverse we will change struct map to zipper data structure. Clojure comes with zip library ( it is short form for zipper ) http://clojure.github.com/clojure/clojure.zip-api.html

We will this zip library to convert xml struct map to zipper data structure.

user> (clojure.zip/xml-zip xml-struct)
[{:tag :friends, :attrs nil, :content [{:tag :person, :attrs nil, :content [{:tag :name, :attrs nil, :content ["Siva"]}]}]} nil]

3) Use Zip-filter library and query zipper data structure.

Now that we have our xml in zipper data structure we could use zip-filter library that is present in clojure.contrib. http://clojure.github.com/clojure-contrib/zip-filter-api.html

user> (clojure.contrib.zip-filter.xml/xml-> zipper-struct :person :name)
("Siva")

Putting this altogether

(ns com.sivajag.utils.xml
  (:import (java.io ByteArrayInputStream))
  (:require [clojure.xml :as xml])
  (:require [clojure.zip :as zip])
  (:require [clojure.contrib.zip-filter.xml :as zf]))

(defn get-struct-map [xml]
  (if-not (empty? xml)
    (let [stream (ByteArrayInputStream. (.getBytes (.trim xml)))]
      (xml/parse stream))))

(defn get-value [xml & tags]
  (apply zf/xml1-> (zip/xml-zip (get-struct-map xml)) (conj (vec tags) zf/text)))

user> (get-value xml :person :name)
"Siva"

Happy Coding!!!

Written by Siva Jagadeesan

June 25, 2010 at 1:57 pm

Posted in Clojure

Tagged with , , ,

Clojure test-is library examples and explanation – Basic

leave a comment »


Clojure contrib comes with a testing framework test-is. Even though it is a small library it does its job. test-is consists many useful functions and macros to help us test our code. I am going to go through some of them and show some examples. Some examples I took it from http://richhickey.github.com/clojure-contrib/test-is-api.html

deftest
Is a macro to define a test function.

Usage

(deftest name & body)

Example

(deftest test-addition
   (is (= (+ 1 2) 3)))

is
It is a generic assertion macro. You will end up using this a lot in your test code.

Usage

(is form)
(is form msg)

Examples

(is (= (+ 1 2) 3))

[or]

user> (is (= (+ 2 1) 3) "should be 3")
true
user> (is (= (+ 3 1) 3) "should be 3")
FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:1)
should be 3
expected: (= (+ 3 1) 3)
actual: (not (= 4 3))
false

This “is” macro could be used to test exceptions too.

(is (thrown? c body))
(is (thrown-with-msg? c re body))

are

Sometimes just using “is” will become tedious.

(deftest test-addition
   (is (= (+ 2 1) 3))
   (is (= (+ 0 0) 0))
   (is (= (+ -1 -1) -2)))

Using “are” we could clean up this test function as

(deftest test-addition
  (are [x y] (= x y)
     3 (+ 2 1)
     0 (+ 0 0)
     -2 (+ -1 -1)))

The only problem using “are” is when a test fails it screws up line number where failure happened.

testing

This is my favorite macro. It allows me organize my tests.

Lets take this test

(deftest test-addition
   (is (= (+ 2 1) 3))
   (is (= (+ 0 0) 0))
   (is (= (+ -1 -1) -2)))

Imagine it is doing some complicated stuff. This test does not express intent. Using “testing” we can rewrite

(deftest test-addition
   (testing "should add positive numbers"
      (is (= (+ 2 1) 3)))
   (testing "should add zeros"
     (is (= (+ 0 0) 0)))
   (testing "should be able to add negative numbers"
      (is (= (+ -1 -1) -2))))

The best part if a test fails it prints the string next to “testing”

(test-addition)

FAIL in (test-addition) (NO_SOURCE_FILE:1)
should be able to add negative numbers
expected: (= (+ -1 -1) -3)
actual: (not (= -2 -3))

Happy testing …

Written by Siva Jagadeesan

June 22, 2010 at 5:47 am

Posted in Clojure

Tagged with , ,

Simple clojure multimethod example

leave a comment »


user> (defmulti teller (fn [m] (:tell m)))
#'user/teller
 user> (defmethod teller :b [m] (:b m))
#
 user> (defmethod teller :a [m] (:a m))
#
 user> (def m {:tell :a :a 1 :b 2})
#'user/m
 user> (teller m)
1
 user> (def m {:tell :b :a 1 :b 2})
#'user/m
 user> (teller m)
2

Written by Siva Jagadeesan

June 16, 2010 at 12:33 am

Posted in Clojure

Tagged with , ,

Follow

Get every new post delivered to your Inbox.

Join 146 other followers

%d bloggers like this: