« back

Refactoring My Echo Server in Clojure

03 Dec 2012

We can make it better.

In my last post about my echo server, we got into adding some conditions. This time, let's do some refactoring and then add some tests to the methods that we extract.

As a refresher, here was the code as it stood before the refactoring.

 1 (defn echo-server [in out]
 2 	(binding [*in* (BufferedReader. (InputStreamReader. in))
 3 			  *out* (OutputStreamWriter. out)]
 4 		(loop [input (read-line)]
 5 			(println 
 6 			    (cond (= "quit" input) "goodbye."
 7 				      (= "what is the meaning of life?" input) "42"
 8 				      :else input))
 9 			    (if (not= "quit" input) (recur (read-line))))))
10 
11 (defn -main []
12 (create-server port echo-server))

I want to be able to test the cond as easily and as isolated as possible. I also want to be able to test the loop/recur with read-line. Let's pull those away from the main echo server to do so. Here are my tests.

 1 (describe "it should be able to process the message based on what is typed to the echo server"
 2 
 3 	(it "should return '42' if you ask the meaning of life"
 4 		(should= "42" (process-message "what is the meaning of life?")))
 5 
 6 	(it "should return 'goodbye' if you type quit"
 7 		(should= "goodbye." (process-message "quit")))
 8 
 9 	(it "should return back what you typed if the input does not match 'what is the meaning of life' or 'quit'"
10 		(should= "hello" (process-message "hello")))
11 )
12 
13 
14 (describe "it should be able to process a stream"
15 
16 	(it "should return 'goodbye.' if quit was typed"
17 		(should= "goodbye.\n"
18 			(with-out-str (with-in-str "quit" (process-stream)))))
19 )

Here is the full implementation. You can see that the cond was pulled away by creating a process message function making that very easy to test. The loop was pulled away as well and can be tested using the with-in-str and with-out-str functions.

 1 (ns echo_server.core
 2 	(:use server.socket))
 3 (import '(java.io BufferedReader InputStreamReader OutputStreamWriter))
 4 
 5 (def port 1234)
 6 
 7 (defn process-message [input]
 8 	(cond (= "quit" input) "goodbye."
 9 		  (= "what is the meaning of life?" input) "42"
10 		 :else input))
11 
12 (defn process-stream []
13 	(loop [input (read-line)]
14 		(println (process-message input))
15 		(if (not= "quit" input) (recur (read-line)))))
16 
17 (defn echo-server [in out]
18 	(binding [*in* (BufferedReader. (InputStreamReader. in))
19 			  *out* (OutputStreamWriter. out)]
20 			(process-stream)))
21 
22 (defn -main []
23 (create-server port echo-server))

Boosh.

comments powered by Disqus