Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

We have CLJS support! #56

Open
wants to merge 8 commits into
base: release
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/target
/classes
/classes-
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
*-init.clj
figwheel_server.log
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ language: clojure
jdk:
- openjdk7
- oraclejdk7
script: lein all test
script: lein all test && lein test:cljs
45 changes: 42 additions & 3 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,48 @@
:distribution :repo}
:deploy-repositories [["releases" :clojars]]
:global-vars {*warn-on-reflection* true}
:profiles {:dev {:dependencies [[org.clojure/clojure "1.7.0"]]}
:dependencies [[org.clojure/clojurescript "1.9.93" ]]
:profiles {:dev {:dependencies [[org.clojure/clojure "1.8.0" ]
[org.clojure/clojurescript "1.9.93" ]
[org.clojure/core.async "0.2.385"] ; To be explicit
[doo "0.1.7" ]] ; CLJS test
:resource-paths ["target/cljs/"]
:plugins [[com.jakemccrary/lein-test-refresh "0.16.0"]
[lein-cljsbuild "1.1.3"
:exclusions [org.clojure/clojurescript]]
[lein-doo "0.1.7"
:exclusions [org.clojure/clojurescript]]
[lein-figwheel "0.5.3-2"
:exclusions [org.clojure/clojure]]]}
:1.4 {:dependencies [[org.clojure/clojure "1.4.0"]]}
:1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]}
:1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}
:1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]}}
:aliases {"all" ["with-profile" "1.4:1.5:1.6:1.7"]})
:1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]}
:1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]}}
:aliases {"all" ["with-profile" #_"1.4:1.5:1.6:1.7:1.8" "1.7:1.8"] ; for CLJC
"test:clj" ["test"]
"test:cljs" ["doo" "phantom" "dev" "once"]
"autotest:cljs" ["doo" "phantom" "dev"]
"autotest:clj" ["test-refresh"]}
:clean-targets ^{:protect false} ["out" "target/cljs"]
:doo {:build "dev"}
:cljsbuild
{:builds
[{:id "dev"
:source-paths ["src" "test"]
:compiler {:main "slingshot.all-tests"
:asset-path "js/compiled/out"
:output-to "target/cljs/dev/slingshot.js"
:output-dir "target/cljs/dev"
:source-map-timestamp true
:optimizations :whitespace}}
{:id "figwheel"
:source-paths ["src" "test"]
:figwheel true
:compiler {:main "slingshot.all-tests"
:asset-path "compiled"
:output-to "target/cljs/figwheel/compiled/slingshot.js"
:output-dir "target/cljs/figwheel/compiled"
:source-map-timestamp true
:optimizations :none}}]}
:figwheel {:http-server-root "figwheel"})
65 changes: 29 additions & 36 deletions src/slingshot/slingshot.clj → src/slingshot/slingshot.cljc
Original file line number Diff line number Diff line change
@@ -1,52 +1,44 @@
(ns slingshot.slingshot
(:require [slingshot.support :as s]))

#?(:clj
(defmacro try+
"Like the try special form, but with enhanced catch clauses and an
optional else clause:

- catch non-Throwable objects thrown by throw+ or data made
throwable by ex-info as well as Throwable objects thrown by
throw or throw+;

- specify objects to catch by class name, key-values, predicate,
or arbitrary selector form;

- destructure the caught object;

- an optional else clause may appear after all catch clauses and
before any finally clause. Its contents will be executed (for
side effects) immediately after the code in the try+ body
completes only if nothing was thrown.

A selector form is a form containing one or more instances of % to
be replaced by the thrown object. If it evaluates to truthy, the
object is caught.

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

<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
access to the contents of a thrown collection.

The local &throw-context is available within try+ catch clauses,
bound to the throw context for the caught object.

See also: throw+, get-throw-context"
[& body]
(let [[expressions catches else finally] (s/parse-try+ body)
threw? (gensym "threw?")]
`(let [~threw? (atom false)]
(try
~@expressions
~@(s/gen-catch catches `throw+ threw?)
~@(s/gen-finally else finally threw?)))))
~@(s/gen-catch &env catches `throw+ threw?)
~@(s/gen-finally else finally threw?))))))

#?(:clj
(defmacro throw+
"Like the throw special form, but can throw any object by wrapping
non-Throwable objects in a Throwable wrapper.
Expand All @@ -62,7 +54,7 @@

- object: required, the object to throw

- cause: optional, a Throwable, the default is:
- cause: optional, a Throwable (CLJS: js/Error), the default is:

- within a try+ catch clause, the the outermost wrapper of
the caught object being processed,
Expand Down Expand Up @@ -91,44 +83,45 @@
(s/stack-trace)
~@args)))
([]
`(s/rethrow)))
`(s/rethrow))))

(defn get-throw-context
"Returns the throw context for an object thrown by throw or throw+
given a Throwable t. Allows callers to access information about any
thrown object as a Clojure map.
given a Throwable (CLJS: js/Error) t. Allows callers to access
information about any thrown object as a Clojure map.

If t or any Throwable in its cause chain wraps a non-Throwable
object thrown by throw+ or data made throwable by ex-info, returns
the associated context with t assoc'd as the value for :throwable,
and the wrapper assoc'd as the value for :wrapper, else returns a
new context based on t.
If t or any Throwable (CLJS: js/Error) in its cause chain wraps a
non-Throwable (CLJS: js/Error) object thrown by throw+ or data made
throwable by ex-info, returns the associated context with t assoc'd
as the value for :throwable, and the wrapper assoc'd as the value for
:wrapper, else returns a new context based on t.

Within a try+ catch clause, prefer using the &throw-context local to
calling get-throw-context explicitly.

A throw context is a map containing:

- for Throwable objects:
- for Throwable (CLJS: js/Error) objects:
:object the object;
:message the message, from .getMessage;
:cause the cause, from .getCause;
:stack-trace the stack trace, from .getStackTrace;
:message the message, from .getMessage (CLJS: .-message);
:cause the cause, from .getCause (CLJS not available);
:stack-trace the stack trace, from .getStackTrace (CLJS: .-stack);
:throwable the object;

- for non-Throwable objects (including data made throwable by ex-info):
- for non-Throwable (CLJS: js/Error) objects
(including data made throwable by ex-info):
:object the object;
:message the message, see throw+, ex-info;
:cause the cause, see throw+, ex-info;
:stack-trace the stack trace, see throw+, ex-info;
:wrapper the Throwable wrapper that carried the object,
see below;
:throwable the outermost Throwable whose cause chain contains
the wrapper, see below;
:wrapper the Throwable (CLJS: js/Error) wrapper that carried the
object, see below;
:throwable the outermost Throwable (CLJS: js/Error) whose cause
chain contains the wrapper, see below;

To throw a non-Throwable object, throw+ or ex-info wraps it in a
Throwable wrapper. The wrapper is available via the :wrapper key in
the throw context.
To throw a non-Throwable (CLJS: js/Error) object, throw+ or ex-info
wraps it in a Throwable (CLJS: js/Error) wrapper. The wrapper is
available via the :wrapper key in the throw context.

Between being thrown and caught, the wrapper may be wrapped by other
exceptions (e.g., instances of RuntimeException or
Expand All @@ -141,9 +134,9 @@
(s/get-context t))

(defn get-thrown-object
"Returns the object thrown by throw or throw+ given a Throwable.
Useful for processing a Throwable outside of a try+ form when the
source of the Throwable may or may not have been throw+ or ex-info.
"Returns the object thrown by throw or throw+ given a Throwable (CLJS: js/Error).
Useful for processing a Throwable (CLJS: js/Error) outside of a try+ form when the
source of the Throwable (CLJS: js/Error) may or may not have been throw+ or ex-info.

See also get-throw-context"
[t]
Expand Down
Loading