Skip to content
Browse files

Upgraded dependencies:

- got rid of last old contrib library c.c.mock, inlined into source tree
- updated access to private vars which got changed in new clojure.java.jdbc
  • Loading branch information...
1 parent 20542de commit 056b2f823e1183e3d8229da3ef1caebba81c3cad @bendlas bendlas committed Sep 7, 2012
Showing with 296 additions and 32 deletions.
  1. +6 −6 project.clj
  2. +10 −13 src/clojureql/connectivity.clj
  3. +0 −1 src/clojureql/core.clj
  4. +4 −5 src/clojureql/internal.clj
  5. +270 −0 test/clojure/contrib/mock.clj
  6. +6 −7 test/clojureql/test/core.clj
View
12 project.clj
@@ -1,15 +1,15 @@
-(defproject clojureql "1.0.3"
+(defproject clojureql "1.0.4-SNAPSHOT"
:description "Superior SQL integration for Clojure"
- :dependencies [[org.clojure/clojure "1.2.1"]
- [org.clojure/core.incubator "0.1.0"]
- [org.clojure.contrib/mock "1.3.0-alpha4"]
- [org.clojure/java.jdbc "0.1.1"]]
+ :dependencies [[org.clojure/clojure "1.4.0"]
+ [org.clojure/core.incubator "0.1.1"]
+ [org.clojure/java.jdbc "0.2.3"]]
:dev-dependencies [[mysql/mysql-connector-java "5.1.17"]
[org.xerial/sqlite-jdbc "3.7.2"]
[postgresql/postgresql "8.4-702.jdbc4"]
[org.apache.derby/derby "10.1.1.0"]]
- :repositories {"clojure-releases" {:url "http://build.clojure.org/releases"}})
+ :repositories {"clojure-releases" {:url "http://build.clojure.org/releases"}
+ "clojure-snapshots" {:url "http://build.clojure.org/snapshots"}})
View
23 src/clojureql/connectivity.clj
@@ -1,5 +1,7 @@
(in-ns 'clojureql.core)
+(require '[clojure.java.jdbc :as jdbc])
+
(def global-connections (atom {}))
(defn open-global
@@ -8,7 +10,7 @@
default global connection."
([specs] (open-global ::clojureql.internal/default-connection specs))
([conn-name specs]
- (let [con (jdbcint/get-connection specs)]
+ (let [con (#'jdbc/get-connection specs)]
(when-let [ac (-> specs :auto-commit)]
(.setAutoCommit con ac))
(swap! global-connections assoc conn-name {:connection con :opts specs}))))
@@ -42,25 +44,20 @@
(io!
(cond
- (find-connection*) ; an already open c.j.jdbc connection takes precedence
+ (jdbc/find-connection) ; an already open c.j.jdbc connection takes precedence
(func)
(map? con-info) ; then we try a passed connection info (presumably associated with some table in the query)
- (with-open [con (jdbcint/get-connection con-info)]
- (binding [jdbcint/*db*
- (assoc jdbcint/*db*
- :connection con
- :level 0
- :rollback (atom false)
- :opts con-info)]
- (.setAutoCommit con (:auto-commit con-info true))
- (func)))
+ (jdbc/with-connection con-info
+ (.setAutoCommit (jdbc/find-connection)
+ (:auto-commit con-info true))
+ (func))
:default ; try global connection
(if-let [con (@clojureql.core/global-connections
(or con-info :clojureql.internal/default-connection))]
- (binding [jdbcint/*db*
- (assoc jdbcint/*db*
+ (binding [jdbc/*db*
+ (assoc @#'jdbc/*db*
:connection (:connection con)
:level 0
:rollback (atom false)
View
1 src/clojureql/core.clj
@@ -12,7 +12,6 @@
[clojureql internal predicates]
[clojure.string :only [join upper-case] :rename {join join-str}]
[clojure.java.jdbc :only [delete-rows]]
- [clojure.java.jdbc.internal :as jdbcint]
[clojure.core.incubator :only [-?> -?>>]]
[clojure.walk :only (postwalk-replace)]))
View
9 src/clojureql/internal.clj
@@ -2,7 +2,6 @@
(:import [java.sql Statement])
(:require
clojure.set
- [clojure.java.jdbc.internal :as jdbcint]
[clojure.java.jdbc :as jdbc])
(:use [clojure.string :only [join split upper-case replace] :rename {join join-str replace replace-str}]
[clojure.core.incubator :only [-?> -?>>]]))
@@ -349,10 +348,10 @@
[[sql & params :as sql-params] func]
(when-not (vector? sql-params)
(throw (Exception. "sql-params must be a vector")))
- (with-open [stmt (jdbc/prepare-statement (:connection jdbcint/*db*) sql)]
+ (with-open [stmt (jdbc/prepare-statement (jdbc/find-connection) sql)]
(doseq [[idx v] (map vector (iterate inc 1) params)]
(.setObject stmt idx v))
- (if-let [fetch-size (-> jdbcint/*db* :opts :fetch-size)]
+ (if-let [fetch-size (-> @#'jdbc/*db* :opts :fetch-size)]
(do
(.setFetchSize stmt fetch-size)
(jdbc/transaction
@@ -386,15 +385,15 @@
open database connection. Each param-group is a seq of values for all of
the parameters."
([sql param-group]
- (with-open [stmt (prepare-statement (:connection jdbcint/*db*) sql)]
+ (with-open [stmt (prepare-statement (jdbc/find-connection) sql)]
(doseq [[idx v] (map vector (iterate inc 1) param-group)]
(.setObject stmt idx v))
(jdbc/transaction
(let [retr (.execute stmt)]
(with-meta [(.getUpdateCount stmt)]
(generated-keys stmt))))))
([sql param-group & param-groups]
- (with-open [stmt (jdbc/prepare-statement (:connection jdbcint/*db*) sql)]
+ (with-open [stmt (jdbc/prepare-statement (jdbc/find-connection) sql)]
(doseq [param-group (cons param-group param-groups)]
(doseq [[idx v] (map vector (iterate inc 1) param-group)]
(.setObject stmt idx v))
View
270 test/clojure/contrib/mock.clj
@@ -0,0 +1,270 @@
+;;; clojure.contrib.mock.clj: mocking/expectation framework for Clojure
+
+;; by Matt Clark
+
+;; Copyright (c) Matt Clark, 2009, 2010. All rights reserved. The use and
+;; distribution terms for this software are covered by the Eclipse Public
+;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can
+;; be found in the file epl-v10.html 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.
+;;------------------------------------------------------------------------------
+
+(ns ^{:author "Matt Clark"
+ :doc "Mock is a function mocking utility inspired by the various ruby and
+java mocking frameworks such as mockito, easymock, and rspec yet designed to
+fit the functional style of clojure."
+ :see-also [["Mocking with clojure.contrib.mock"
+ "http://notesonclojure.blogspot.com/2010/06/mocking-with-clojurecontribmock.html"]]}
+ clojure.contrib.mock)
+
+;; positions, ripped from clojure.contrib.seq
+
+(defn indexed
+ "Returns a lazy sequence of [index, item] pairs, where items come
+ from 's' and indexes count up from zero.
+
+ (indexed '(a b c d)) => ([0 a] [1 b] [2 c] [3 d])"
+ [s]
+ (map vector (iterate inc 0) s))
+
+(defn positions
+ "Returns a lazy sequence containing the positions at which pred
+ is true for items in coll."
+ [pred coll]
+ (for [[idx elt] (indexed coll) :when (pred elt)] idx))
+
+;;------------------------------------------------------------------------------
+;; These are the error condition functions. Override them to integrate into
+;; the test framework of your choice, or to simply customize error handling.
+
+(defn report-problem
+ {:dynamic true
+ :doc "Override this to customize error handling. By default, prints an
+error message to *out*."}
+ ([function expected actual]
+ (report-problem function expected actual "Expectation not met."))
+ ([function expected actual message]
+ (prn (str message " Function name: " function
+ " expected: " expected " actual: " actual))))
+
+(defn no-matching-function-signature
+ {:dynamic true
+ :doc "Override to customize unmatched function signature errors.
+Prints an error message to *out* by default."}
+ [function expected actual]
+ (report-problem function expected actual
+ "No matching real function signature for given argument count."))
+
+(defn unexpected-args
+ {:dynamic true
+ :doc "Override to customize unexpected argument errors. Prints an error
+message to *out* by default."}
+ [function expected actual i]
+ (report-problem function expected actual
+ (str "Argument " i " has an unexpected value for function.")))
+
+(defn incorrect-invocation-count
+ {:dynamic true
+ :doc "Override to customize incorrect invocation counts. Prints an error
+to *out* by default."}
+ [function expected actual]
+ (report-problem function expected actual "Unexpected invocation count."))
+
+
+;;------------------------------------------------------------------------------
+;; Internal Functions - ignore these
+
+
+(defn- has-arg-count-match?
+ {:doc "Given the sequence of accepted argument vectors for a function
+returns true if at least one matches the given-count value."
+ :skip-wiki true}
+ [arg-lists given-count]
+ (some #(let [[ind] (positions #{'&} %)]
+ (if ind
+ (>= given-count ind)
+ (= (count %) given-count)))
+ arg-lists))
+
+
+(defn has-matching-signature?
+ ^{:doc "Calls no-matching-function-signature if no match is found for the
+given function. If no argslist meta data is available for the function, it is
+not called."
+ :skip-wiki true}
+ [fn-name args]
+ (let [arg-count (count args)
+ arg-lists (:arglists (meta (resolve fn-name)))]
+ (if (and arg-lists (not (has-arg-count-match? arg-lists arg-count)))
+ (no-matching-function-signature fn-name arg-lists args))))
+
+
+(defn make-arg-checker
+ ^{:doc "Creates the argument verifying function for a replaced dependency
+within the expectation bound scope. These functions take the additional
+argument of the name of the replaced function, then the rest of their args.
+It is designed to be called from the mock function generated in the first
+argument of the mock info object created by make-mock."
+ :skip-wiki true}
+ [arg-preds arg-pred-forms]
+ (let [sanitized-preds (map (fn [v] (if (fn? v) v #(= v %))) arg-preds)]
+ (fn [fn-name & args]
+ (every? true?
+ (map (fn [pred arg pred-form i] (if (pred arg) true
+ (unexpected-args fn-name
+ pred-form arg i)))
+ sanitized-preds args arg-pred-forms (iterate inc 0))))))
+
+
+(defn make-count-checker
+ ^{:doc "creates the count checker that is invoked at the end of an
+expectation, after the code under test has all been executed. The function
+returned takes the name of the associated dependency and the invocation
+count as arguments."
+ :skip-wiki true}
+ [pred pred-form]
+ (let [pred-fn (if (integer? pred) #(= pred %) pred)]
+ (fn [fn-name v] (if (pred-fn v) true
+ (incorrect-invocation-count fn-name pred-form v)))))
+
+(defn make-mock
+ ^{:doc "creates a vector containing the following information for the
+named function:
+1. dependent function replacement - verifies signature, calls arg checker
+increases count, returns return value.
+2. an atom containing the invocation count
+3. the invocation count checker function
+4. a symbol of the name of the function being replaced."
+ :skip-wiki true}
+ [fn-name expectation-hash]
+ {:pre [(map? expectation-hash)
+ (symbol? fn-name)]}
+ (let [arg-checker (or (expectation-hash :has-args) (fn [& args] true))
+ count-atom (atom 0)
+ ret-fn (or
+ (expectation-hash :calls)
+ (fn [& args] (expectation-hash :returns)))]
+ [(fn [& args]
+ (has-matching-signature? fn-name args)
+ (apply arg-checker fn-name args)
+ (swap! count-atom inc)
+ (apply ret-fn args))
+ count-atom
+ (or (expectation-hash :times) (fn [fn-name v] true))
+ fn-name]))
+
+
+(defn validate-counts
+ ^{:doc "given the sequence of all mock data for the expectation, simply
+calls the count checker for each dependency."
+ :skip-wiki true}
+ [mock-data] (doseq [[mfn i checker fn-name] mock-data] (checker fn-name @i)))
+
+(defn- make-bindings
+ ^{:skip-wiki true}
+ [expect-bindings mock-data-sym]
+ `[~@(interleave (map #(first %) (partition 2 expect-bindings))
+ (map (fn [i] `(nth (nth ~mock-data-sym ~i) 0))
+ (range (quot (count expect-bindings) 2))))])
+
+
+;;------------------------------------------------------------------------------
+;; These are convenience functions to improve the readability and use of this
+;; library. Useful in expressions such as:
+;; (expect [dep-fn1 (times (more-than 1) (returns 15)) etc)
+
+;; best used in the times function
+(defn once "Predicate which ensures argument equals 1. For times function."
+ [x] (= 1 x))
+
+(defn never "Predicate which ensures argument is zero. For times function."
+ [x] (zero? x))
+
+(defn more-than "Returns a predicate which ensures argument is more than x."
+ [x] #(< x %))
+
+(defn less-than "Returns a predicate which ensures argument is less than x."
+ [x] #(> x %))
+
+(defn between "Returns a predicate which ensures argument is between x and y."
+ [x y] #(and (< x %) (> y %)))
+
+(defn anything "Syntactic sugar for has-args function. A predicate which
+always returns true."
+ [x] true)
+
+
+;;------------------------------------------------------------------------------
+;; The following functions can be used to build up the expectation hash.
+
+(defn ^{:arglists '([ret-value expectation-hash?])} returns
+ "Creates or associates to an existing expectation hash the :returns key with
+a value to be returned by the expectation after a successful invocation
+matching its expected arguments (if applicable)."
+
+ ([val] (returns val {}))
+ ([val expectation-hash]
+ (assoc expectation-hash :returns val)))
+
+
+(defn ^{:arglists '([replacement-fn expectation-hash?])} calls
+ "Creates or associates to an existing expectation hash the :calls key with a
+function that will be called with the given arguments. The return value from
+this function will be returned by the expected function. If both this
+and :returns are specified, the return value of :calls will have precedence."
+
+ ([val] (calls val {}))
+ ([val expectation-hash]
+ {:pre [(fn? val)]}
+ (assoc expectation-hash :calls val)))
+
+
+(defmacro ^{:arglists '([pred-coll expectation-hash?])} has-args
+ "Creates or associates to an existing expectation hash the :has-args key with
+a value corresponding to a function that will either return true if its
+argument expectations are met or throw an exception with the details of the
+first failed argument it encounters.
+Only specify as many predicates as you are interested in verifying. The rest
+of the values are safely ignored."
+
+ ([arg-pred-forms] `(has-args ~arg-pred-forms {}))
+ ([arg-pred-forms expectation-hash]
+ {:pre [(vector? arg-pred-forms)]}
+ `(assoc ~expectation-hash :has-args
+ (make-arg-checker ~arg-pred-forms '~arg-pred-forms))))
+
+
+(defmacro ^{:arglists '([n expectation-hash?])} times
+ "Creates or associates to an existing expectation hash the :times key with a
+value corresponding to a predicate function which expects an integer value.
+Also, an integer can be specified, in which case the times will only be an
+exact match. The times check is called at the end of an expect expression to
+validate that an expected dependency function was called the expected
+number of times."
+
+ ([times-fn] `(times ~times-fn {}))
+ ([times-fn expectation-hash]
+ `(assoc ~expectation-hash :times (make-count-checker ~times-fn '~times-fn))))
+
+
+;-------------------------------------------------------------------------------
+; The main expect macro.
+(defmacro expect
+ "Use expect to redirect calls to dependent functions that are made within the
+code under test. Instead of calling the functions that would normally be used
+temporary stubs are used, which can verify function parameters and call counts.
+Return values of overridden functions can also be specified as needed."
+
+ [expect-bindings & body]
+ {:pre [(vector? expect-bindings)
+ (even? (count expect-bindings))]}
+ (let [mock-data (gensym "mock-data_")]
+ `(let [~mock-data (map (fn [args#]
+ (apply clojure.contrib.mock/make-mock args#))
+ ~(cons 'list (map (fn [[n m]]
+ (vector (list 'quote n) m))
+ (partition 2 expect-bindings))))]
+ (with-redefs ~(make-bindings expect-bindings mock-data) ~@body)
+ (clojure.contrib.mock/validate-counts ~mock-data) true)))
View
13 test/clojureql/test/core.clj
@@ -2,8 +2,7 @@
(:refer-clojure
:exclude [compile take drop sort distinct conj! disj! case])
(:use [clojureql.internal :only [update-or-insert-vals update-vals]]
- [clojure.java.jdbc :only [with-connection]]
- [clojure.java.jdbc.internal :only [find-connection*]]
+ [clojure.java.jdbc :only [with-connection find-connection]]
clojure.test
clojureql.core
clojure.contrib.mock)
@@ -29,7 +28,7 @@
(defn resolve-table-db [spec-on-tble]
(with-cnx spec-on-tble
- (get (find-connection*) :id)))
+ (get (find-connection) :id)))
(deftest connection-sources
(testing "missing connection info"
@@ -376,18 +375,18 @@
(testing "update!"
(expect [update-vals (has-args [:users ["(id = ?)" 1] {:name "Bob"}])
- find-connection* (returns true)]
+ find-connection (returns true)]
(update! (table :users) (where (= :id 1)) {:name "Bob"}))
(expect [update-vals (has-args [:users ["(salary IS NULL)"] {:salary 1000}])
- find-connection* (returns true)]
+ find-connection (returns true)]
(update! (table :users) (where (= :salary nil)) {:salary 1000})))
(testing "update-in!"
(expect [update-or-insert-vals (has-args [:users ["(id = ?)" 1] {:name "Bob"}])
- find-connection* (returns true)]
+ find-connection (returns true)]
(update-in! (table :users) (where (= :id 1)) {:name "Bob"}))
(expect [update-or-insert-vals (has-args [:users ["(salary IS NULL)"] {:salary 1000}])
- find-connection* (returns true)]
+ find-connection (returns true)]
(update-in! (table :users) (where (= :salary nil)) {:salary 1000})))
(testing "difference"

0 comments on commit 056b2f8

Please sign in to comment.
Something went wrong with that request. Please try again.