22
Clojure in the Wild

Clojure in the Wild

Embed Size (px)

Citation preview

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 Redirectsredirect(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)]))))