Skip to content

Commit

Permalink
You can use checkers on the optional args
Browse files Browse the repository at this point in the history
  • Loading branch information
marick committed Mar 20, 2013
1 parent 89030e8 commit 5f8bc43
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 34 deletions.
29 changes: 17 additions & 12 deletions src/midje/parsing/3_from_lexical_maps/from_fake_maps.clj
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -16,20 +16,25 @@
(fn [actual] (extended-= actual (exactly expected))) (fn [actual] (extended-= actual (exactly expected)))
(fn [actual] (extended-= actual expected)))) (fn [actual] (extended-= actual expected))))


(defn mkfn:arg-matchers-with-arity (defn mkfn:arglist-matcher-fixed-arity
"Generates a function that returns true if all the matchers return true for the actual args it's passed." "Generates a function that returns true if all the matchers return true for the actual args it's passed."
[matchers] [& arg-descriptions]
(fn [actual-args] (fn [actual-args]
(let [arg-matchers (map mkfn:arg-matcher matchers)] (extended-list-= actual-args
(and (= (count actual-args) (count arg-matchers)) (map mkfn:arg-matcher arg-descriptions))))
(extended-list-= actual-args arg-matchers)))))

(defn mkfn:arglist-matcher-allowing-optional-args
(defn mkfn:arg-matchers-without-arity "Generates a function that attempts to match required and optional args."
"Generates a function that returns true if all the matchers return true but it ignores arity matching." [& arg-descriptions]
[matchers] (let [required-count (- (count arg-descriptions) 2)
(fn [actual-args] required-arglist-descriptions (take required-count arg-descriptions)
(let [arg-matchers (map mkfn:arg-matcher matchers)] rest-arg-description (last arg-descriptions)
(extended-list-= actual-args arg-matchers)))) required-arg-matchers (map mkfn:arg-matcher required-arglist-descriptions)
rest-arg-matcher (mkfn:arg-matcher rest-arg-description)]
(fn [actual-args]
(let [[required-actual rest-actual] (split-at required-count actual-args)]
(and (extended-list-= required-actual required-arg-matchers)
(extended-= rest-actual rest-arg-matcher))))))


(defmulti mkfn:result-supplier (fn [arrow & _] arrow)) (defmulti mkfn:result-supplier (fn [arrow & _] arrow))


Expand Down
17 changes: 6 additions & 11 deletions src/midje/parsing/lexical_maps.clj
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -64,16 +64,11 @@
;; A fake map describes all or part of a temporary rebinding of a var with a function that ;; A fake map describes all or part of a temporary rebinding of a var with a function that
;; captures invocations and also returns canned values. ;; captures invocations and also returns canned values.


(defn- arity-matcher? [arg] (defn- choose-mkfn-for-arglist-matcher [arg-matchers]
(boolean (= arg (symbol "&")))) (letfn [(allows-optional-args? [args] (any? #(= % (symbol "&")) args))]

This comment has been minimized.

Copy link
@josephwilk

josephwilk Mar 21, 2013

Contributor

Can I ask why letfn is preferred over small functions?

Curious for the reason as in my head small functions are more readable.


(if (allows-optional-args? arg-matchers)
(defn- some-ignore-arity-matcher? [args] `(from-fake-maps/mkfn:arglist-matcher-allowing-optional-args ~@arg-matchers)
(boolean (some arity-matcher? args))) `(from-fake-maps/mkfn:arglist-matcher-fixed-arity ~@arg-matchers))))

(defn- arg-matchers-form [arg-matchers]
(if (some-ignore-arity-matcher? arg-matchers)
`(from-fake-maps/mkfn:arg-matchers-without-arity ~(vec (remove arity-matcher? arg-matchers)))
`(from-fake-maps/mkfn:arg-matchers-with-arity ~(vec arg-matchers))))


(defn fake [call-form fnref args arrow result overrides] (defn fake [call-form fnref args arrow result overrides]
(let [source-details `{:call-form '~call-form (let [source-details `{:call-form '~call-form
Expand All @@ -87,7 +82,7 @@
:var ~(fnref/as-var-form fnref) :var ~(fnref/as-var-form fnref)
:value-at-time-of-faking (if (bound? ~(fnref/as-var-form fnref)) :value-at-time-of-faking (if (bound? ~(fnref/as-var-form fnref))
~(fnref/as-form-to-fetch-var-value fnref)) ~(fnref/as-form-to-fetch-var-value fnref))
:arg-matchers ~(arg-matchers-form args) :arg-matchers ~(choose-mkfn-for-arglist-matcher args)
:result-supplier (from-fake-maps/mkfn:result-supplier ~arrow ~result) :result-supplier (from-fake-maps/mkfn:result-supplier ~arrow ~result)
:times :default ; Default allows for a more attractive error in the most common case. :times :default ; Default allows for a more attractive error in the most common case.


Expand Down
8 changes: 3 additions & 5 deletions test/as_documentation/prerequisites__the_basics.clj
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -233,13 +233,11 @@
(provided (provided
(letter desired-letter & anything) => ..letter-result..))) (letter desired-letter & anything) => ..letter-result..)))



(fact "You can even apply a checker to the &rest argument"


(future-fact "You can even apply a checker to the &rest argument"
(find-letter) => ..letter-result.. (find-letter) => ..letter-result..
(provided (provided
(letter & (as-checker (fn [actual] (prn actual) true))) => ..letter-result..)) (letter & (just "this" "doesn't" "match")) => ..bogus-result.. :times 0
(letter & (just "x" "y" "z")) => ..letter-result..))






Expand Down
24 changes: 18 additions & 6 deletions test/midje/parsing/3_from_lexical_maps/t_from_fake_maps.clj
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@


(tabular (tabular
(facts "the arg matcher maker handles functions specially" (facts "the arg matcher maker handles functions specially"
((mkfn:arg-matchers-with-arity [?expected]) [?actual]) => ?result ((apply mkfn:arglist-matcher-fixed-arity [?expected]) [?actual]) => ?result)
((mkfn:arg-matchers-without-arity [?expected]) [?actual]) => ?result)
?expected ?actual ?result ?expected ?actual ?result
1 1 TRUTHY 1 1 TRUTHY
1 odd? falsey 1 odd? falsey
Expand All @@ -34,12 +33,25 @@ anything odd? TRUTHY
odd? odd? TRUTHY odd? odd? TRUTHY
odd? 3 falsey) odd? 3 falsey)


(fact "false if there is an arity mismatch" (fact "sometimes an arglist must be matched exactly"
((mkfn:arg-matchers-with-arity [anything]) [1 2 3]) => falsey) ((mkfn:arglist-matcher-fixed-arity 1 2) [1 ]) => falsey
((mkfn:arglist-matcher-fixed-arity 1 2) [1 2 ]) => truthy
((mkfn:arglist-matcher-fixed-arity 1 2) [1 2 3]) => falsey)

(fact "an arglist can allow rest args"
((mkfn:arglist-matcher-allowing-optional-args 1 2 & anything) [1 ]) => falsey
((mkfn:arglist-matcher-allowing-optional-args 1 2 & anything) [1 2 ]) => truthy
((mkfn:arglist-matcher-allowing-optional-args 1 2 & anything) [1 2 3]) => truthy


(fact "ignoring arity mismatches" (fact "the required args are treated the same as the fixed-arity case"
((mkfn:arg-matchers-without-arity [anything]) [1 2 3]) => TRUTHY) ( (mkfn:arglist-matcher-allowing-optional-args 1 even? & anything) [1 2 3]) => falsey
( (mkfn:arglist-matcher-allowing-optional-args 1 (as-checker even?) & anything) [1 2 3]) => truthy)


(fact "the argument after the & is treated as a checker"
((mkfn:arglist-matcher-allowing-optional-args 1 2 & (as-checker empty?)) [1 2]) => truthy
((mkfn:arglist-matcher-allowing-optional-args 1 2 & empty? ) [1 2]) => falsey
((mkfn:arglist-matcher-allowing-optional-args 1 2 & (as-checker empty?)) [1 2 3]) => falsey))

(facts "about result suppliers used" (facts "about result suppliers used"
"returns identity for =>" "returns identity for =>"
(let [arrow "=>"] (let [arrow "=>"]
Expand Down

0 comments on commit 5f8bc43

Please sign in to comment.