Skip to content

Commit

Permalink
[issue scgilardi#23] generalize key-value pair selector to key-values
Browse files Browse the repository at this point in the history
  • Loading branch information
scgilardi committed Mar 20, 2012
1 parent d9f53d1 commit 577f979
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 21 deletions.
3 changes: 2 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,5 @@
support for testing that the proper exception is thrown (issue 21)
- remove replace-all and % substitution in source in favor of using
let to bind % (issue 22)

- key-value pair -> key-values, generalize key-value selector to
allow any even number of items in the vector (issue 23)
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ Enhanced throw and catch for Clojure
- a **class name**: (e.g., `RuntimeException`, `my.clojure.record`),
matches any instance of that class, or

- a **key-value pair**: (two element vector), matches objects where
`(get object key)` returns `val`, or
- a **key-values**: (e.g., `[key val & kvs]`), matches objects
where `(and (= (get object key) val ...))`, or

- a **predicate**: (function of one argument like `map?`, `set?`),
matches any Object for which the predicate returns a truthy
Expand All @@ -45,12 +45,12 @@ Enhanced throw and catch for Clojure
`%` to be replaced by the thrown object, matches any object for
which the form evaluates to truthy.

- the class name, key-value pair, and predicate selectors are
- the class name, key-values, and predicate selectors are
shorthand for these selector forms:

`<class name> => (instance? <class name> %)`

`[<key> <val>] => (= (get % <key>) <val>)`
`[<key> <val> & <kvs>] => (and (= (get % <key>) <val>) ...)`

`<predicate> => (<predicate> %)`

Expand Down
10 changes: 5 additions & 5 deletions src/slingshot/slingshot.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- catch non-Throwable objects thrown by throw+ as well as
Throwable objects thrown by throw or throw+;
- specify objects to catch by class name, key-value pair,
- specify objects to catch by class name, key-values,
predicate, or arbitrary selector form;
- destructure the caught object;
Expand All @@ -20,12 +20,12 @@
be replaced by the thrown object. If it evaluates to truthy, the
object is caught.
The class name, key-value pair, and predicate selectors are
The class name, key-values, and predicate selectors are
shorthand for these selector forms:
<class name> => (instance? <class name> %)
[<key> <val>] => (= (get % <key>) <val>)
<predicate> => (<predicate> %)
<class name> => (instance? <class name> %)
[<key> <val> & <kvs>] => (and (= (get % <key>) <val>) ...)
<predicate> => (<predicate> %)
The binding form in a try+ catch clause is not required to be a
simple symbol. It is subject to destructuring which allows easy
Expand Down
14 changes: 7 additions & 7 deletions src/slingshot/support.clj
Original file line number Diff line number Diff line change
Expand Up @@ -153,20 +153,20 @@
[(class-name []
(and (symbol? selector) (class? (resolve selector))
`(instance? ~selector ~'%)))
(key-value []
(key-values []
(and (vector? selector)
(if (= (count selector) 2)
(let [[key val] selector]
`(= (get ~'% ~key) ~val))
(throw-arg "key-value selector: %s does not match: %s"
(pr-str selector) "[key val]"))))
(if (even? (count selector))
`(and ~@(for [[key val] (partition 2 selector)]
`(= (get ~'% ~key) ~val)))
(throw-arg "key-values selector: %s does not match: %s"
(pr-str selector) "[key val & kvs]"))))
(selector-form []
(and (seq? selector) (appears-within? '% selector)
selector))
(predicate []
`(~selector ~'%))]
`(let [~'% (:object ~'&throw-context)]
~(or (class-name) (key-value) (selector-form) (predicate)))))
~(or (class-name) (key-values) (selector-form) (predicate)))))
(cond-expression [binding-form expressions]
`(let [~binding-form (:object ~'&throw-context)]
~@expressions))
Expand Down
15 changes: 11 additions & 4 deletions test/slingshot/test/slingshot.clj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
(catch [:a-key 4] e#
[:key-yields-value e#])

;; by multiple-key-value
(catch [:key1 4 :key2 5] e#
[:keys-yield-values e#])

;; by key present
(catch (contains? ~'% :a-key) e#
[:key-is-present e#])
Expand All @@ -70,7 +74,6 @@
[:pred-map e# (meta e#)])))

(deftest test-try+

(testing "catch by class derived from Throwable"
(testing "treat throwables exactly as throw does, interop with try/throw"
(is (= [:class-exception exception-1]
Expand All @@ -90,9 +93,13 @@
(is (= [:class-exception-record exception-record-1]
(mega-try (throw+ exception-record-1)))))

(testing "catch by key, with optional value"
(is (= [:key-is-present #{:a-key}] (mega-try (throw+ #{:a-key}))))
(is (= [:key-yields-value {:a-key 4}] (mega-try (throw+ {:a-key 4})))))
(testing "catch by key is present"
(is (= [:key-is-present #{:a-key}] (mega-try (throw+ #{:a-key})))))

(testing "catch by keys and values"
(is (= [:key-yields-value {:a-key 4}] (mega-try (throw+ {:a-key 4}))))
(is (= [:keys-yield-values {:key1 4 :key2 5}]
(mega-try (throw+ {:key1 4 :key2 5})))))

(testing "catch by clojure type with optional hierarchy"
(is (= [:type-sphere ::sphere a-sphere] (mega-try (throw+ a-sphere))))
Expand Down

0 comments on commit 577f979

Please sign in to comment.