aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscgilardi <scgilardi@gmail.com>2008-07-22 06:23:10 +0000
committerscgilardi <scgilardi@gmail.com>2008-07-22 06:23:10 +0000
commitd2cfb3ff67a2ca4774928664758a5f8880cf285c (patch)
tree5b77c03fce628fae5bec41e8520a882690a55424
parent40aae31f03c00834cd4d356f829eb7fb0e7e9215 (diff)
provide namespace directories and contents for def and sql
-rw-r--r--def/def.clj74
-rw-r--r--sql/sql.clj126
2 files changed, 200 insertions, 0 deletions
diff --git a/def/def.clj b/def/def.clj
new file mode 100644
index 00000000..1708eb94
--- /dev/null
+++ b/def/def.clj
@@ -0,0 +1,74 @@
+;; Copyright (c) Stephen C. Gilardi. All rights reserved.
+;; The use and distribution terms for this software are covered by the
+;; Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+;; which can be found in the file CPL.TXT at the root of this distribution.
+;; By using this software in any fashion, you are agreeing to be bound by
+;; the terms of this license.
+;; You must not remove this notice, or any other, from this software.
+;;
+;; File: def.clj
+;;
+;; def.clj provides variants of def that make including doc strings and
+;; making private definitions more succinct.
+;;
+;; scgilardi (gmail)
+;; 17 May 2008
+
+(clojure/in-ns 'clojure-contrib.def)
+(clojure/refer 'clojure)
+
+(defmacro init-once
+ "Initializes a var exactly once. The var must already exist."
+ [var init]
+ `(let [v# (resolve '~var)]
+ (when-not (.isBound v#)
+ (.bindRoot v# ~init))))
+
+(defmacro defvar
+ "Defines a var with an optional intializer and doc string"
+ ([name]
+ (list `def name))
+ ([name init]
+ (list `def name init))
+ ([name init doc]
+ (list `def (with-meta name (assoc (meta name) :doc doc)) init)))
+
+(defmacro defunbound
+ "Defines an unbound var with optional doc string"
+ ([name]
+ (list `def name))
+ ([name doc]
+ (list `def (with-meta name (assoc (meta name) :doc doc)))))
+
+(defmacro defmacro-
+ "Same as defmacro but yields a private definition"
+ [name & decls]
+ (list* `defmacro (with-meta name (assoc (meta name) :private true)) decls))
+
+(defmacro defvar-
+ "Same as defvar but yields a private definition"
+ [name & decls]
+ (list* `defvar (with-meta name (assoc (meta name) :private true)) decls))
+
+(defmacro defunbound-
+ "Same as defunbound but yields a private definition"
+ [name & decls]
+ (list* `defunbound (with-meta name (assoc (meta name) :private true)) decls))
+
+(defmacro defstruct-
+ "Same as defstruct but yields a private definition"
+ [name & decls]
+ (list* `defstruct (with-meta name (assoc (meta name) :private true)) decls))
+
+(defmacro defalias
+ "Defines an alias for a var: a new var with the same value and metadata
+ as another with the exception of :namespace, :name, :file, :line, and
+ optionally :doc which are those of new var."
+ ([name orig]
+ `(let [v# (def ~name ~orig)]
+ (. v# (setMeta (merge (meta #'~orig) (meta #'~name))))
+ v#))
+ ([name orig doc]
+ `(let [v# (def ~name ~orig)]
+ (. v# (setMeta (merge (meta #'~orig) (assoc (meta #'~name) :doc ~doc))))
+ v#)))
diff --git a/sql/sql.clj b/sql/sql.clj
new file mode 100644
index 00000000..6b32efd6
--- /dev/null
+++ b/sql/sql.clj
@@ -0,0 +1,126 @@
+;; Copyright (c) Stephen C. Gilardi. All rights reserved.
+;; The use and distribution terms for this software are covered by the
+;; Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+;; which can be found in the file CPL.TXT at the root of this distribution.
+;; By using this software in any fashion, you are agreeing to be bound by
+;; the terms of this license.
+;; You must not remove this notice, or any other, from this software.
+;;
+;; sql.clj
+;;
+;; A Clojure interface to sql databases via jdbc
+;;
+;; scgilardi (gmail)
+;; 23 April 2008
+
+(clojure/in-ns 'clojure-contrib.sql)
+(clojure/refer 'clojure)
+
+(import '(java.sql DriverManager Connection PreparedStatement ResultSet))
+
+(defn get-connection
+ "Attempts to get a connection to a database via a jdbc URL"
+ [subprotocol db-name]
+ (let [url (str "jdbc:" subprotocol ":" db-name)]
+ (.getConnection DriverManager url)))
+
+(defmacro with-connection
+ "Evaluates body in the context of a connection to a database. Any updates
+ are committed as one transaction after evaluating body or rolled back on
+ any uncaught exception."
+ [con init & body]
+ `(with-open ~con ~init
+ (try
+ (.setAutoCommit ~con false))
+ ~@body
+ (.commit ~con)
+ (catch Exception e#
+ (.rollback ~con)
+ (throw (Exception. "transaction rolled back" e#)))))
+
+(defn execute-commands
+ "Executes a sequence of SQL commands that do not return results"
+ [con commands]
+ (with-open stmt (.createStatement con)
+ (doseq cmd commands
+ (.addBatch stmt cmd))
+ (.executeBatch stmt)))
+
+(defn execute-prepared-statement
+ "Executes a prepared statement with a sequence of parameter sets"
+ [con sql sets]
+ (with-open stmt (.prepareStatement con sql)
+ (doseq set sets
+ (doseq [index value] (map vector (iterate inc 1) set)
+ (.setObject stmt index value))
+ (.addBatch stmt ))
+ (.executeBatch stmt)))
+
+(defmacro with-query-results
+ "Executes a query and then evaluates body repeatedly with rec bound to
+ each of the generated results in turn"
+ [rec con sql & body]
+ `(with-open stmt# (.prepareStatement ~con ~sql)
+ (with-open rset# (.executeQuery stmt#)
+ (doseq ~rec (resultset-seq rset#)
+ ~@body))))
+
+(comment
+
+ ;; Examples
+
+ ;; Simple tests of sql.clj using derby as a JDBC provider.
+ ;;
+ ;; Substituting a different database should only affect the definition
+ ;; of 'db' below (and perhaps suggest the need for more variations of
+ ;; get-connection).
+
+(clojure/in-ns 'sql-test)
+(clojure/refer 'clojure)
+
+(lib/use sql)
+
+(.forName Class "org.apache.derby.jdbc.EmbeddedDriver")
+
+(defn db []
+ (get-connection "derby" "/tmp/test-derby.db;create=true"))
+
+(defn db-drop []
+ (with-connection con (db)
+ (try
+ (execute-commands con
+ ["drop table fruit"])
+ (catch Exception e))))
+
+(defn db-write []
+ (db-drop)
+ (with-connection con (db)
+ (execute-commands con
+ ["create table fruit (name varchar(32), appearance varchar(32), cost int, grade real)"])
+ (execute-prepared-statement con
+ "insert into fruit values (?, ?, ?, ?)"
+ [["Apple" "red" 59 87]
+ ["Banana" "yellow" 29 92.2]
+ ["Peach" "fuzzy" 139 90.0]
+ ["Orange" "juicy" 89 88.6]])))
+
+(defn db-read []
+ (with-connection con (db)
+ (with-query-results rec con
+ "select * from fruit"
+ (println rec))))
+
+(defn db-grade-a []
+ (with-connection con (db)
+ (with-query-results rec con
+ "select name, cost from fruit where grade >= 90"
+ (println rec))))
+
+(defn db-exception []
+ (with-connection con (db)
+ (execute-prepared-statement con
+ "insert into fruit (name, appearance) values (?, ?)"
+ [["Grape" "yummy"]
+ ["Pear" "bruised"]])
+ (throw (Exception. "an exception"))))
+)