SlideShare a Scribd company logo
1 of 22
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

More Related Content

What's hot

UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013Michelangelo van Dam
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Michelangelo van Dam
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KThomas Fuchs
 
JS Fest 2018. Сергей Пузанков. E2E-тестирование фронтенда c Hermione
JS Fest 2018. Сергей Пузанков. E2E-тестирование фронтенда c HermioneJS Fest 2018. Сергей Пузанков. E2E-тестирование фронтенда c Hermione
JS Fest 2018. Сергей Пузанков. E2E-тестирование фронтенда c HermioneJSFestUA
 
Basic Tutorial of React for Programmers
Basic Tutorial of React for ProgrammersBasic Tutorial of React for Programmers
Basic Tutorial of React for ProgrammersDavid Rodenas
 
Testing most things in JavaScript - LeedsJS 31/05/2017
Testing most things in JavaScript - LeedsJS 31/05/2017Testing most things in JavaScript - LeedsJS 31/05/2017
Testing most things in JavaScript - LeedsJS 31/05/2017Colin Oakley
 
How to write easy-to-test JavaScript
How to write easy-to-test JavaScriptHow to write easy-to-test JavaScript
How to write easy-to-test JavaScriptYnon Perek
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyIgor Napierala
 
Better Bullshit Driven Development [SeleniumCamp 2017]
Better Bullshit Driven Development [SeleniumCamp 2017]Better Bullshit Driven Development [SeleniumCamp 2017]
Better Bullshit Driven Development [SeleniumCamp 2017]automician
 
Testing Javascript with Jasmine
Testing Javascript with JasmineTesting Javascript with Jasmine
Testing Javascript with JasmineTim Tyrrell
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 
Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Michelangelo van Dam
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express MiddlewareMorris Singer
 
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptGuy Royse
 
QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"
QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"
QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"GeeksLab Odessa
 
Performance Optimization In Angular 2
Performance Optimization In Angular 2Performance Optimization In Angular 2
Performance Optimization In Angular 2Eyal Vardi
 

What's hot (20)

UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013UA testing with Selenium and PHPUnit - PFCongres 2013
UA testing with Selenium and PHPUnit - PFCongres 2013
 
Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013Workshop quality assurance for php projects - ZendCon 2013
Workshop quality assurance for php projects - ZendCon 2013
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
 
Advanced Django
Advanced DjangoAdvanced Django
Advanced Django
 
JS Fest 2018. Сергей Пузанков. E2E-тестирование фронтенда c Hermione
JS Fest 2018. Сергей Пузанков. E2E-тестирование фронтенда c HermioneJS Fest 2018. Сергей Пузанков. E2E-тестирование фронтенда c Hermione
JS Fest 2018. Сергей Пузанков. E2E-тестирование фронтенда c Hermione
 
Basic Tutorial of React for Programmers
Basic Tutorial of React for ProgrammersBasic Tutorial of React for Programmers
Basic Tutorial of React for Programmers
 
Testing most things in JavaScript - LeedsJS 31/05/2017
Testing most things in JavaScript - LeedsJS 31/05/2017Testing most things in JavaScript - LeedsJS 31/05/2017
Testing most things in JavaScript - LeedsJS 31/05/2017
 
How to write easy-to-test JavaScript
How to write easy-to-test JavaScriptHow to write easy-to-test JavaScript
How to write easy-to-test JavaScript
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishy
 
Nativescript angular
Nativescript angularNativescript angular
Nativescript angular
 
My java file
My java fileMy java file
My java file
 
Celery
CeleryCelery
Celery
 
Better Bullshit Driven Development [SeleniumCamp 2017]
Better Bullshit Driven Development [SeleniumCamp 2017]Better Bullshit Driven Development [SeleniumCamp 2017]
Better Bullshit Driven Development [SeleniumCamp 2017]
 
Testing Javascript with Jasmine
Testing Javascript with JasmineTesting Javascript with Jasmine
Testing Javascript with Jasmine
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012Quality Assurance for PHP projects - ZendCon 2012
Quality Assurance for PHP projects - ZendCon 2012
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express Middleware
 
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
 
QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"
QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"
QA Lab: тестирование ПО. Яков Крамаренко: "KISS Automation"
 
Performance Optimization In Angular 2
Performance Optimization In Angular 2Performance Optimization In Angular 2
Performance Optimization In Angular 2
 

Viewers also liked

3장 자동적으로 움직이는 게임 에이전트 생성법_2
3장 자동적으로 움직이는 게임 에이전트 생성법_23장 자동적으로 움직이는 게임 에이전트 생성법_2
3장 자동적으로 움직이는 게임 에이전트 생성법_2suitzero
 
xUnitTestPattern/chapter16
xUnitTestPattern/chapter16xUnitTestPattern/chapter16
xUnitTestPattern/chapter16suitzero
 
2.2.4 순환목록
2.2.4 순환목록2.2.4 순환목록
2.2.4 순환목록suitzero
 
1.4.2 코루틴연습문제
1.4.2 코루틴연습문제1.4.2 코루틴연습문제
1.4.2 코루틴연습문제suitzero
 
HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3suitzero
 
The gravitational N -body pro
The gravitational N -body proThe gravitational N -body pro
The gravitational N -body prosuitzero
 
3장 자동적으로 움직이는 게임 에이전트 생성법
3장 자동적으로 움직이는 게임 에이전트 생성법3장 자동적으로 움직이는 게임 에이전트 생성법
3장 자동적으로 움직이는 게임 에이전트 생성법suitzero
 

Viewers also liked (7)

3장 자동적으로 움직이는 게임 에이전트 생성법_2
3장 자동적으로 움직이는 게임 에이전트 생성법_23장 자동적으로 움직이는 게임 에이전트 생성법_2
3장 자동적으로 움직이는 게임 에이전트 생성법_2
 
xUnitTestPattern/chapter16
xUnitTestPattern/chapter16xUnitTestPattern/chapter16
xUnitTestPattern/chapter16
 
2.2.4 순환목록
2.2.4 순환목록2.2.4 순환목록
2.2.4 순환목록
 
1.4.2 코루틴연습문제
1.4.2 코루틴연습문제1.4.2 코루틴연습문제
1.4.2 코루틴연습문제
 
HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3HolubOnPatterns/chapter3_3
HolubOnPatterns/chapter3_3
 
The gravitational N -body pro
The gravitational N -body proThe gravitational N -body pro
The gravitational N -body pro
 
3장 자동적으로 움직이는 게임 에이전트 생성법
3장 자동적으로 움직이는 게임 에이전트 생성법3장 자동적으로 움직이는 게임 에이전트 생성법
3장 자동적으로 움직이는 게임 에이전트 생성법
 

Similar to Clojure in the Wild

Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenerytoddbr
 
Data driven testing using Integrant & Spec
Data driven testing using Integrant & SpecData driven testing using Integrant & Spec
Data driven testing using Integrant & SpecLeon Mergen
 
Deixa para depois, Procrastinando com Celery em Python
Deixa para depois, Procrastinando com Celery em PythonDeixa para depois, Procrastinando com Celery em Python
Deixa para depois, Procrastinando com Celery em PythonAdriano Petrich
 
HTML5 for the Silverlight Guy
HTML5 for the Silverlight GuyHTML5 for the Silverlight Guy
HTML5 for the Silverlight GuyDavid Padbury
 
A la découverte de TypeScript
A la découverte de TypeScriptA la découverte de TypeScript
A la découverte de TypeScriptDenis Voituron
 
Apache Spark in your likeness - low and high level customization
Apache Spark in your likeness - low and high level customizationApache Spark in your likeness - low and high level customization
Apache Spark in your likeness - low and high level customizationBartosz Konieczny
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Tsuyoshi Yamamoto
 
Updates since :has() was enabled in Chrome
Updates since :has() was enabled in ChromeUpdates since :has() was enabled in Chrome
Updates since :has() was enabled in ChromeIgalia
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with ClojureDmitry Buzdin
 
Building frameworks over Selenium
Building frameworks over SeleniumBuilding frameworks over Selenium
Building frameworks over SeleniumCristian COȚOI
 
Real life-coffeescript
Real life-coffeescriptReal life-coffeescript
Real life-coffeescriptDavid Furber
 
Protractor framework – how to make stable e2e tests for Angular applications
Protractor framework – how to make stable e2e tests for Angular applicationsProtractor framework – how to make stable e2e tests for Angular applications
Protractor framework – how to make stable e2e tests for Angular applicationsLudmila Nesvitiy
 
Our challenge for Bulkload reliability improvement
Our challenge for Bulkload reliability  improvementOur challenge for Bulkload reliability  improvement
Our challenge for Bulkload reliability improvementSatoshi Akama
 
Exploring Clojurescript
Exploring ClojurescriptExploring Clojurescript
Exploring ClojurescriptLuke Donnet
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing UpDavid Padbury
 

Similar to Clojure in the Wild (20)

Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
KISS Automation.py
KISS Automation.pyKISS Automation.py
KISS Automation.py
 
Data driven testing using Integrant & Spec
Data driven testing using Integrant & SpecData driven testing using Integrant & Spec
Data driven testing using Integrant & Spec
 
Play vs Rails
Play vs RailsPlay vs Rails
Play vs Rails
 
Deixa para depois, Procrastinando com Celery em Python
Deixa para depois, Procrastinando com Celery em PythonDeixa para depois, Procrastinando com Celery em Python
Deixa para depois, Procrastinando com Celery em Python
 
HTML5 for the Silverlight Guy
HTML5 for the Silverlight GuyHTML5 for the Silverlight Guy
HTML5 for the Silverlight Guy
 
A la découverte de TypeScript
A la découverte de TypeScriptA la découverte de TypeScript
A la découverte de TypeScript
 
Apache Spark in your likeness - low and high level customization
Apache Spark in your likeness - low and high level customizationApache Spark in your likeness - low and high level customization
Apache Spark in your likeness - low and high level customization
 
J query training
J query trainingJ query training
J query training
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
 
Updates since :has() was enabled in Chrome
Updates since :has() was enabled in ChromeUpdates since :has() was enabled in Chrome
Updates since :has() was enabled in Chrome
 
Java script
Java scriptJava script
Java script
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
 
Building frameworks over Selenium
Building frameworks over SeleniumBuilding frameworks over Selenium
Building frameworks over Selenium
 
Real life-coffeescript
Real life-coffeescriptReal life-coffeescript
Real life-coffeescript
 
Protractor framework – how to make stable e2e tests for Angular applications
Protractor framework – how to make stable e2e tests for Angular applicationsProtractor framework – how to make stable e2e tests for Angular applications
Protractor framework – how to make stable e2e tests for Angular applications
 
Finding Clojure
Finding ClojureFinding Clojure
Finding Clojure
 
Our challenge for Bulkload reliability improvement
Our challenge for Bulkload reliability  improvementOur challenge for Bulkload reliability  improvement
Our challenge for Bulkload reliability improvement
 
Exploring Clojurescript
Exploring ClojurescriptExploring Clojurescript
Exploring Clojurescript
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 

Clojure in the Wild

  • 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 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)
  • 6. 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.
  • 7. 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}
  • 8. 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!
  • 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 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
  • 11. 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)
  • 12. 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>})
  • 13. 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"]))
  • 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 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)))))
  • 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-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>
  • 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-to location) (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)]))))