Skip to content

Commit

Permalink
vcr-clj.core/with-cassette supports :arg-transformer option
Browse files Browse the repository at this point in the history
I added a new `:arg-transformer` option that is analogous to the existing
`:return-transformer` option. During both recording and playback, the original
arguments are interepted and replaced by `(apply return-transformer args)`, and
these transformed arguments are passed to `arg-key-fn`, `recordable?`, and the
recorded function.

This feature is intended to enable serialization of HTTP request bodies for
gfredericks#17
  • Loading branch information
chairmank committed Apr 14, 2017
1 parent 62c90a7 commit 070241b
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 17 deletions.
46 changes: 29 additions & 17 deletions src/vcr_clj/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,24 @@
true)

(defn ^:private build-wrapped-fn
[record-fn {:keys [var arg-key-fn recordable? return-transformer]
:or {arg-key-fn vector
[record-fn {:keys [var arg-transformer arg-key-fn recordable? return-transformer]
:or {arg-transformer vector
arg-key-fn vector
recordable? (constantly true)
return-transformer identity}}]
(let [orig-fn (deref var)
the-var-name (var-name var)
wrapped (fn [& args]
(if-not (and *recording?* (apply recordable? args))
(apply orig-fn args)
(let [res (binding [*recording?* false]
(return-transformer (apply orig-fn args)))
call {:var-name the-var-name
:arg-key (apply arg-key-fn args)
:return res}]
(record-fn call)
res)))]
(let [args* (apply arg-transformer args)]
(if-not (and *recording?* (apply recordable? args*))
(apply orig-fn args*)
(let [res (binding [*recording?* false]
(return-transformer (apply orig-fn args*)))
call {:var-name the-var-name
:arg-key (apply arg-key-fn args*)
:return res}]
(record-fn call)
res))))]
(add-meta-from wrapped orig-fn)))

;; TODO: add the ability to configure whether out-of-order
Expand Down Expand Up @@ -91,17 +93,19 @@
(let [the-playbacker (playbacker cassette :key)
redeffings
(into {}
(for [{:keys [var arg-key-fn recordable?]
:or {arg-key-fn vector
(for [{:keys [var arg-transformer arg-key-fn recordable?]
:or {arg-transformer vector
arg-key-fn vector
recordable? (constantly true)}}
specs
:let [orig (deref var)
the-var-name (var-name var)
wrapped (fn [& args]
(if (apply recordable? args)
(let [k (apply arg-key-fn args)]
(:return (the-playbacker the-var-name k)))
(apply orig args)))]]
(let [args* (apply arg-transformer args)]
(if (apply recordable? args*)
(let [k (apply arg-key-fn args*)]
(:return (the-playbacker the-var-name k)))
(apply orig args*))))]]
[var (add-meta-from wrapped orig)]))]
(with-redefs-fn redeffings func)))

Expand Down Expand Up @@ -133,6 +137,14 @@
;; the rest are optional
:arg-transformer
a function with the same arg signature as the var,
which is expected to returns a vector of equivalent
arguments. During recording/playback, the original
arguments to the function call are passed through this
transformer, and the transformed arguments are passed
to arg-key-fn, recordable? and the recorded function.
Defaults to clojure.core/identity.
:arg-key-fn a function of the same arguments as the var that is
expected to return a value that can be stored and
compared for equality to the expected call. Defaults
Expand Down
22 changes: 22 additions & 0 deletions test/vcr_clj/test/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
;; some test fns
(defn plus [a b] (+ a b))
(defn increment [x] (inc x))
(defn put-foo [m v] (assoc m :foo v))

(deftest basic-test
(with-spy [plus]
Expand Down Expand Up @@ -87,6 +88,27 @@
:arg-key-fn #(mod % 2)}]
(is (= 42 (increment 29)))))

(deftest arg-transformer-test
(with-spy [put-foo]
(with-cassette :shebang
[{:var #'put-foo
:arg-transformer (fn [m v]
(is (= [{} 42] [m v]))
[(vary-meta m assoc :arg-transformer true) v])
:recordable? (fn [m v]
(is (= [{} 42] [m v]))
(is (:arg-transformer (meta m)))
true)
:arg-key-fn (fn [m v]
(is (= [{} 42] [m v]))
(is (:arg-transformer (meta m)))
[m v])}]
(is (= {:foo 42} (put-foo {} 42)))
(is (= 1 (count (calls put-foo))))
(is (= [{} 42]
(:args (first (calls put-foo)))))
(is (-> (calls put-foo) first :args first meta :arg-transformer)))))

(defn self-caller
"Multi-arity function that calls itself when called with one argument"
([x]
Expand Down

0 comments on commit 070241b

Please sign in to comment.