Clojure in the Wild
Contents
Unit Test
Acessing Relational data
Web app
Test with :test
Test with :test
examples/index_of_any.clj
(test #'index-of-any)
⇒ :ok
(defn
#^{:test (fn []
(assert (nil? (busted))))}
busted [] "busted")
(test #'busted)
⇒ java.lang.Exception: Assert failed: (nil? (busted))
Test with test-is
Write test with this!!
(clojure.contrib.test-is/deftest testname & forms)
Use 'is' Macro instead of assertions.
(clojure.contrib.test-is/is form message?)
Ready to run all tests?
(clojure.contrib.test-is/run-tests & namespaces)
Test with test-is
(deftest test-that-demonstrates-failure
(is (= 5 (+ 2 2))))
(run-tests)
Testing user
FAIL in (test-that-demonstrates-failure) (NO_SOURCE_FILE:5)
expected: (= 5 (+ 2 2))
actual: (not= 5 4)
Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
Test with test-is
Exception handling with (is (thrown? ))
(deftest test-divide-by-zero
(is (thrown? ArithmeticException (/ 5 0))))
(run-tests)
Testing examples.test
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
{:type :summary, :test 1, :pass 1, :fail 0, :error 0}
Other Options for Test
All Clojure sequences implement Java collection interfaces.
All Clojure functions implement Callable and Runnable.
So, you can write your tests with any Java-compatible test
framework:
JUnit, TestNG, or even EasyB or JTestR.
Good luck!
Data Access
clojure.contrib.sql -> thin wrappers for JDBC
The entry point for sql is the with-connection macro:
(clojure.contrib.sql/with-connection db-spec & body)
(use 'clojure.contrib.sql)
; START: db
; replace "snippet-db" with a full path!
(def db {:classname "org.hsqldb.jdbcDriver"
:subprotocol "hsqldb"
:subname "file:snippet-db"})
; END: db
; db-spec -> JDBC's DriverManager
Data Access
Create Table by create-table function
(clojure.contrib.sql/create-table name & column-specs)
(use 'clojure.contrib.sql)
(defn create-snippets []
(create-table :snippets
[:id :int "IDENTITY" "PRIMARY KEY" ]
[:body :varchar "NOT NULL" ]
[:created_at :datetime]))
(with-connection db (create-snippets))
(0)
(with-connection db (create-snippets)))
java.lang.Exception: transaction rolled back: failed batch
Data Access
Insert-values functions for adds rows
(clojure.contrib.sql/insert-values table column-names & values)
(defn now [] (java.sql.Timestamp. (.getTime (java.util.Date.))))
(defn insert-snippets []
(let [timestamp (now)]
(seq
(insert-values :snippets
[:body :created_at]
["(println :boo)" timestamp]
["(defn foo [] 1)" timestamp]))))
(with-connection db (insert-snippets)))
(1 1)
Data Access
issues some sql and then executes
the forms in body with results
->
with-query-results
(with-query-results results sql & body)
(defn print-snippets []
(with-query-results res ["select * from snippets" ]
(println res)))
(with-connection db (print-snippets))
({:id 0, :body (println :boo),
:created_at #<Timestamp 2009-01-03 11:40:19.985>}
{:id 1, :body (defn foo [] 1),
:created_at #<Timestamp 2009-01-03 11:40:19.985>})
Data Access
; Broken!
(defn select-snippets []
(with-query-results res ["select * from snippets" ] res))
(with-connection db (select-snippets)))
java.sql.SQLException: No data is available
JDBC results can be lazy & select-snippets return lazy seqs
(defn sql-query [q]
(with-query-results res q (doall res)))
(with-connection db (sql-query ["select body from snippets"]))
Data Access
transaction macro
(clojure.contrib.sql/transaction & body)
; ID of the new snippet
(defn last-created-id [] (first (vals (first (sql-query ["CALL IDENTITY()" ])))))
(defn insert-snippet [body]
(with-connection db
(transaction
(insert-values :snippets
[:body :created_at]
[body (now)])
(last-created-id))))
(insert-snippet "(+ 1 1)")
) 4
(insert-snippet "(ref true)")
) 5
Other library Options
Higher level tool options
-> use a java framework(Hibernate,iBatis,ActiveRecord..)
-> use a clojure-specific persistence libraries(clj-
record,clojureql)
clj-record example
(ns clj-record.test.model.manufacturer
(:require [clj-record.core :as cljrec])
(cljrec/init-model
(has-many products)
(validates name "empty!" #(not (empty? %)))
(validates name "starts with whitespace!" #(not (re-find #"^s" %)))
(validates name "ends with whitespace!" #(not (re-find #"s$" %)))
(validates grade "negative!" #(or (nil? %) (>= % 0))))
clojureql example
(execute (sql
(query [id name] developers.employees (and (> id 5) (< id 8)))))
Web DevelopmentCompojure & Jetty web server
simple routes create by defroutes
(compojure/defroutes name doc & routes)
routes (HTTP-VERB url-pattern body)
run-server
(compojure/run-server options? & paths-and-servlets)
(use 'compojure)
(defroutes snippet-app
"Create and view snippets."
(GET "/ping" "pong" )
(ANY "*"
(page-not-found)))
(run-server {:port 8080}
"/*" (servlet snippet-app))
Building HTML
test-area function
(compojure/text-area options? name value?)
html function to actual html
(compojure/html & trees)
(use 'compojure) (text-area "body"))
[:textarea {:id "body", :name "body"} nil]
(println (html (text-area "body")))
<textarea id="body" name="body"></textarea>
Building HTML
(compojure/form-to handler & body)
(compojure/submit-button options? & text)
(defn new-snippet []
(html
(form-to [:post "/" ]
(text-area {:rows 20 :cols 73} "body" )
[:br]
(submit-button "Save" ))))
(println (new-snippet))
| <form action="/" method="POST">
| <textarea cols="73" id="body" name="body" rows="20"></textarea>
| <br />
| <input type="submit" value="Save" />
| </form>
Posts and Redirects
redirect
(compojure/redirect-to location)
(defn create-snippet [body]
(if-let [id (insert-snippet body)]
(redirect-to (str "/" id))
(redirect-to "/" )))
Finishing Touches
(compojure/serve-file root? path)
(GET "/public/*" (or (serve-file (params :*)) :next))
(compojure/include-js & scripts)
(compojure/include-css & scripts)
(defn layout [title & body]
(html
[:head
[:title title]
(include-js "/public/javascripts/code-highlighter.js"
"/public/javascripts/clojure.js" )
(include-css "/public/stylesheets/code-highlighter.css" )]
[:body
[:h2 title]
body]))
Finishing Touches
(defn show-snippet [id]
(layout (str "Snippet " id)
(let [snippet (select-snippet id)]
(html
[:div [:pre [:code.clojure (:body snippet)]]]
[:div (:created_at snippet)]))))
Clojure in the Wild

Clojure in the Wild

  • 1.
  • 2.
  • 3.
  • 4.
    Test with :test examples/index_of_any.clj (test#'index-of-any) ⇒ :ok (defn #^{:test (fn [] (assert (nil? (busted))))} busted [] "busted") (test #'busted) ⇒ java.lang.Exception: Assert failed: (nil? (busted))
  • 5.
    Test with test-is Writetest with this!! (clojure.contrib.test-is/deftest testname & forms) Use 'is' Macro instead of assertions. (clojure.contrib.test-is/is form message?) Ready to run all tests? (clojure.contrib.test-is/run-tests & namespaces)
  • 6.
    Test with test-is (deftesttest-that-demonstrates-failure (is (= 5 (+ 2 2)))) (run-tests) Testing user FAIL in (test-that-demonstrates-failure) (NO_SOURCE_FILE:5) expected: (= 5 (+ 2 2)) actual: (not= 5 4) Ran 1 tests containing 1 assertions. 1 failures, 0 errors.
  • 7.
    Test with test-is Exceptionhandling with (is (thrown? )) (deftest test-divide-by-zero (is (thrown? ArithmeticException (/ 5 0)))) (run-tests) Testing examples.test Ran 1 tests containing 1 assertions. 0 failures, 0 errors. {:type :summary, :test 1, :pass 1, :fail 0, :error 0}
  • 8.
    Other Options forTest All Clojure sequences implement Java collection interfaces. All Clojure functions implement Callable and Runnable. So, you can write your tests with any Java-compatible test framework: JUnit, TestNG, or even EasyB or JTestR. Good luck!
  • 9.
    Data Access clojure.contrib.sql ->thin wrappers for JDBC The entry point for sql is the with-connection macro: (clojure.contrib.sql/with-connection db-spec & body) (use 'clojure.contrib.sql) ; START: db ; replace "snippet-db" with a full path! (def db {:classname "org.hsqldb.jdbcDriver" :subprotocol "hsqldb" :subname "file:snippet-db"}) ; END: db ; db-spec -> JDBC's DriverManager
  • 10.
    Data Access Create Tableby create-table function (clojure.contrib.sql/create-table name & column-specs) (use 'clojure.contrib.sql) (defn create-snippets [] (create-table :snippets [:id :int "IDENTITY" "PRIMARY KEY" ] [:body :varchar "NOT NULL" ] [:created_at :datetime])) (with-connection db (create-snippets)) (0) (with-connection db (create-snippets))) java.lang.Exception: transaction rolled back: failed batch
  • 11.
    Data Access Insert-values functionsfor adds rows (clojure.contrib.sql/insert-values table column-names & values) (defn now [] (java.sql.Timestamp. (.getTime (java.util.Date.)))) (defn insert-snippets [] (let [timestamp (now)] (seq (insert-values :snippets [:body :created_at] ["(println :boo)" timestamp] ["(defn foo [] 1)" timestamp])))) (with-connection db (insert-snippets))) (1 1)
  • 12.
    Data Access issues somesql and then executes the forms in body with results -> with-query-results (with-query-results results sql & body) (defn print-snippets [] (with-query-results res ["select * from snippets" ] (println res))) (with-connection db (print-snippets)) ({:id 0, :body (println :boo), :created_at #<Timestamp 2009-01-03 11:40:19.985>} {:id 1, :body (defn foo [] 1), :created_at #<Timestamp 2009-01-03 11:40:19.985>})
  • 13.
    Data Access ; Broken! (defnselect-snippets [] (with-query-results res ["select * from snippets" ] res)) (with-connection db (select-snippets))) java.sql.SQLException: No data is available JDBC results can be lazy & select-snippets return lazy seqs (defn sql-query [q] (with-query-results res q (doall res))) (with-connection db (sql-query ["select body from snippets"]))
  • 14.
    Data Access transaction macro (clojure.contrib.sql/transaction& body) ; ID of the new snippet (defn last-created-id [] (first (vals (first (sql-query ["CALL IDENTITY()" ]))))) (defn insert-snippet [body] (with-connection db (transaction (insert-values :snippets [:body :created_at] [body (now)]) (last-created-id)))) (insert-snippet "(+ 1 1)") ) 4 (insert-snippet "(ref true)") ) 5
  • 15.
    Other library Options Higherlevel tool options -> use a java framework(Hibernate,iBatis,ActiveRecord..) -> use a clojure-specific persistence libraries(clj- record,clojureql) clj-record example (ns clj-record.test.model.manufacturer (:require [clj-record.core :as cljrec]) (cljrec/init-model (has-many products) (validates name "empty!" #(not (empty? %))) (validates name "starts with whitespace!" #(not (re-find #"^s" %))) (validates name "ends with whitespace!" #(not (re-find #"s$" %))) (validates grade "negative!" #(or (nil? %) (>= % 0)))) clojureql example (execute (sql (query [id name] developers.employees (and (> id 5) (< id 8)))))
  • 16.
    Web DevelopmentCompojure &Jetty web server simple routes create by defroutes (compojure/defroutes name doc & routes) routes (HTTP-VERB url-pattern body) run-server (compojure/run-server options? & paths-and-servlets) (use 'compojure) (defroutes snippet-app "Create and view snippets." (GET "/ping" "pong" ) (ANY "*" (page-not-found))) (run-server {:port 8080} "/*" (servlet snippet-app))
  • 17.
    Building HTML test-area function (compojure/text-areaoptions? name value?) html function to actual html (compojure/html & trees) (use 'compojure) (text-area "body")) [:textarea {:id "body", :name "body"} nil] (println (html (text-area "body"))) <textarea id="body" name="body"></textarea>
  • 18.
    Building HTML (compojure/form-to handler& body) (compojure/submit-button options? & text) (defn new-snippet [] (html (form-to [:post "/" ] (text-area {:rows 20 :cols 73} "body" ) [:br] (submit-button "Save" )))) (println (new-snippet)) | <form action="/" method="POST"> | <textarea cols="73" id="body" name="body" rows="20"></textarea> | <br /> | <input type="submit" value="Save" /> | </form>
  • 19.
    Posts and Redirects redirect (compojure/redirect-tolocation) (defn create-snippet [body] (if-let [id (insert-snippet body)] (redirect-to (str "/" id)) (redirect-to "/" )))
  • 20.
    Finishing Touches (compojure/serve-file root?path) (GET "/public/*" (or (serve-file (params :*)) :next)) (compojure/include-js & scripts) (compojure/include-css & scripts) (defn layout [title & body] (html [:head [:title title] (include-js "/public/javascripts/code-highlighter.js" "/public/javascripts/clojure.js" ) (include-css "/public/stylesheets/code-highlighter.css" )] [:body [:h2 title] body]))
  • 21.
    Finishing Touches (defn show-snippet[id] (layout (str "Snippet " id) (let [snippet (select-snippet id)] (html [:div [:pre [:code.clojure (:body snippet)]]] [:div (:created_at snippet)]))))