This is an automated email from the git hooks/post-receive script. apoikos pushed a commit to branch master in repository clout-clojure.
commit b68560fd0be73b68703f3ff3952c9382d0c25e56 Author: Apollon Oikonomopoulos <apoi...@debian.org> Date: Sat Aug 5 09:24:41 2017 -0400 New upstream version 2.1.2 --- .gitignore | 9 ++ .travis.yml | 6 ++ LICENSE.html | 261 +++++++++++++++++++++++++++++++++++++++++++++++ README.md | 136 ++++++++++++++++++++++++ project.clj | 13 +++ src/clout/core.clj | 126 +++++++++++++++++++++++ test/clout/core_test.clj | 127 +++++++++++++++++++++++ 7 files changed, 678 insertions(+) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e04714b --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/target +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..75560d1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: clojure +lein: lein2 +script: lein2 with-profile dev:dev,1.4:dev,1.5:dev,1.6:dev,1.7 test +jdk: + - openjdk7 + - openjdk6 diff --git a/LICENSE.html b/LICENSE.html new file mode 100644 index 0000000..813c07d --- /dev/null +++ b/LICENSE.html @@ -0,0 +1,261 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> +<title>Eclipse Public License - Version 1.0</title> +<style type="text/css"> + body { + size: 8.5in 11.0in; + margin: 0.25in 0.5in 0.25in 0.5in; + tab-interval: 0.5in; + } + p { + margin-left: auto; + margin-top: 0.5em; + margin-bottom: 0.5em; + } + p.list { + margin-left: 0.5in; + margin-top: 0.05em; + margin-bottom: 0.05em; + } + </style> + +</head> + +<body lang="EN-US"> + +<p align=center><b>Eclipse Public License - v 1.0</b></p> + +<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR +DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS +AGREEMENT.</p> + +<p><b>1. DEFINITIONS</b></p> + +<p>"Contribution" means:</p> + +<p class="list">a) in the case of the initial Contributor, the initial +code and documentation distributed under this Agreement, and</p> +<p class="list">b) in the case of each subsequent Contributor:</p> +<p class="list">i) changes to the Program, and</p> +<p class="list">ii) additions to the Program;</p> +<p class="list">where such changes and/or additions to the Program +originate from and are distributed by that particular Contributor. A +Contribution 'originates' from a Contributor if it was added to the +Program by such Contributor itself or anyone acting on such +Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) +are not derivative works of the Program.</p> + +<p>"Contributor" means any person or entity that distributes +the Program.</p> + +<p>"Licensed Patents" mean patent claims licensable by a +Contributor which are necessarily infringed by the use or sale of its +Contribution alone or when combined with the Program.</p> + +<p>"Program" means the Contributions distributed in accordance +with this Agreement.</p> + +<p>"Recipient" means anyone who receives the Program under +this Agreement, including all Contributors.</p> + +<p><b>2. GRANT OF RIGHTS</b></p> + +<p class="list">a) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free copyright license to reproduce, prepare derivative works +of, publicly display, publicly perform, distribute and sublicense the +Contribution of such Contributor, if any, and such derivative works, in +source code and object code form.</p> + +<p class="list">b) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, +offer to sell, import and otherwise transfer the Contribution of such +Contributor, if any, in source code and object code form. This patent +license shall apply to the combination of the Contribution and the +Program if, at the time the Contribution is added by the Contributor, +such addition of the Contribution causes such combination to be covered +by the Licensed Patents. The patent license shall not apply to any other +combinations which include the Contribution. No hardware per se is +licensed hereunder.</p> + +<p class="list">c) Recipient understands that although each Contributor +grants the licenses to its Contributions set forth herein, no assurances +are provided by any Contributor that the Program does not infringe the +patent or other intellectual property rights of any other entity. Each +Contributor disclaims any liability to Recipient for claims brought by +any other entity based on infringement of intellectual property rights +or otherwise. As a condition to exercising the rights and licenses +granted hereunder, each Recipient hereby assumes sole responsibility to +secure any other intellectual property rights needed, if any. For +example, if a third party patent license is required to allow Recipient +to distribute the Program, it is Recipient's responsibility to acquire +that license before distributing the Program.</p> + +<p class="list">d) Each Contributor represents that to its knowledge it +has sufficient copyright rights in its Contribution, if any, to grant +the copyright license set forth in this Agreement.</p> + +<p><b>3. REQUIREMENTS</b></p> + +<p>A Contributor may choose to distribute the Program in object code +form under its own license agreement, provided that:</p> + +<p class="list">a) it complies with the terms and conditions of this +Agreement; and</p> + +<p class="list">b) its license agreement:</p> + +<p class="list">i) effectively disclaims on behalf of all Contributors +all warranties and conditions, express and implied, including warranties +or conditions of title and non-infringement, and implied warranties or +conditions of merchantability and fitness for a particular purpose;</p> + +<p class="list">ii) effectively excludes on behalf of all Contributors +all liability for damages, including direct, indirect, special, +incidental and consequential damages, such as lost profits;</p> + +<p class="list">iii) states that any provisions which differ from this +Agreement are offered by that Contributor alone and not by any other +party; and</p> + +<p class="list">iv) states that source code for the Program is available +from such Contributor, and informs licensees how to obtain it in a +reasonable manner on or through a medium customarily used for software +exchange.</p> + +<p>When the Program is made available in source code form:</p> + +<p class="list">a) it must be made available under this Agreement; and</p> + +<p class="list">b) a copy of this Agreement must be included with each +copy of the Program.</p> + +<p>Contributors may not remove or alter any copyright notices contained +within the Program.</p> + +<p>Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution.</p> + +<p><b>4. COMMERCIAL DISTRIBUTION</b></p> + +<p>Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use of +the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create +potential liability for other Contributors. Therefore, if a Contributor +includes the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and +indemnify every other Contributor ("Indemnified Contributor") +against any losses, damages and costs (collectively "Losses") +arising from claims, lawsuits and other legal actions brought by a third +party against the Indemnified Contributor to the extent caused by the +acts or omissions of such Commercial Contributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In +order to qualify, an Indemnified Contributor must: a) promptly notify +the Commercial Contributor in writing of such claim, and b) allow the +Commercial Contributor to control, and cooperate with the Commercial +Contributor in, the defense and any related settlement negotiations. The +Indemnified Contributor may participate in any such claim at its own +expense.</p> + +<p>For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those +performance claims and warranties, and if a court requires any other +Contributor to pay any damages as a result, the Commercial Contributor +must pay those damages.</p> + +<p><b>5. NO WARRANTY</b></p> + +<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, +ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to +the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations.</p> + +<p><b>6. DISCLAIMER OF LIABILITY</b></p> + +<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT +NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p> + +<p><b>7. GENERAL</b></p> + +<p>If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further action +by the parties hereto, such provision shall be reformed to the minimum +extent necessary to make such provision valid and enforceable.</p> + +<p>If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other +software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the +date such litigation is filed.</p> + +<p>All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of time +after becoming aware of such noncompliance. If all Recipient's rights +under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.</p> + +<p>Everyone is permitted to copy and distribute copies of this +Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The +Agreement Steward reserves the right to publish new versions (including +revisions) of this Agreement from time to time. No one other than the +Agreement Steward has the right to modify this Agreement. The Eclipse +Foundation is the initial Agreement Steward. The Eclipse Foundation may +assign the responsibility to serve as the Agreement Steward to a +suitable separate entity. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version +of the Agreement is published, Contributor may elect to distribute the +Program (including its Contributions) under the new version. Except as +expressly stated in Sections 2(a) and 2(b) above, Recipient receives no +rights or licenses to the intellectual property of any Contributor under +this Agreement, whether expressly, by implication, estoppel or +otherwise. All rights in the Program not expressly granted under this +Agreement are reserved.</p> + +<p>This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No party +to this Agreement will bring a legal action under this Agreement more +than one year after the cause of action arose. Each party waives its +rights to a jury trial in any resulting litigation.</p> + +</body> + +</html> diff --git a/README.md b/README.md new file mode 100644 index 0000000..2bc53e8 --- /dev/null +++ b/README.md @@ -0,0 +1,136 @@ +# Clout + +[![Build Status](https://travis-ci.org/weavejester/clout.svg?branch=master)](https://travis-ci.org/weavejester/clout) + +Clout is a library for matching [Ring][1] HTTP requests. It uses the same +routing syntax as used by popular Ruby web frameworks like Ruby on Rails and +Sinatra. + +[1]: https://github.com/ring-clojure/ring + +## Installation + +Add the following to your project.clj dependencies: + +```clj +[clout "2.1.2"] +``` + +## Usage + +Require Clout in the normal way: + +```clj +(require '[clout.core :as clout]) +``` + +These following examples also make use of the [Ring-Mock][2] library +to generate Ring request maps: + +[2]: https://github.com/ring-clojure/ring-mock + +```clj +(require '[ring.mock.request :as mock]) +``` + +Routes can match by keyword: + +```clj +(clout/route-matches + "/article/:title" + (mock/request :get "/article/clojure")) + +=> {:title "clojure"} +``` + +Or with wildcards: + +```clj +(clout/route-matches + "/public/*" + (mock/request :get "/public/style/screen.css")) + +=> {:* "style/screen.css"} +``` + +Clout can also match absolute routes: + +```clj +(clout/route-matches + "http://subdomain.example.com/" + (mock/request :get "http://subdomain.example.com/")) + +=> {} +``` +And scheme-relative routes: + +```clj +(clout/route-matches + "//subdomain.example.com/" + (mock/request :get "http://subdomain.example.com/")) + +=> {} + +(clout/route-matches + "//subdomain.example.com/" + (mock/request :get "https://subdomain.example.com/")) + +=> {} +``` + +Clout supports both keywords and wildcards. Keywords (like ":title") will +match any character but the following: `/ . , ; ?`. Wildcards (*) will match +anything. + +If a route does not match, nil is returned: + +```clj +(clout/route-matches "/products" (mock/request :get "/articles")) + +=> nil +``` + +For additional performance, you can choose to pre-compile a route: + +```clj +(def user-route + (clout/route-compile "/user/:id")) + +(clout/route-matches user-route (mock/request :get "/user/10")) + +=> {:id "10"} +``` + +When compiling a route, you can specify a map of regular expressions to use +for different keywords. This allows more specific routing: + +```clj +(def user-route + (clout/route-compile "/user/:id" {:id #"\d+"})) + +(clout/route-matches user-route (mock/request :get "/user/10")) + +=> {:user "10"} + +(clout/route-matches user-route (mock/request :get "/user/jsmith")) + +=> nil +``` + +You can also specify regular expressions inline in braces after the +keyword: + +```clj +(def user-route + (clout/route-compile "/user/:id{\\d+}")) +``` + +Note that regular expression escape sequences (like `\d`) need to be +double-escaped when placed inline in a string. + +## License + +Copyright © 2015 James Reeves + +Distributed under the Eclipse Public License either version 1.0 or (at +your option) any later version. diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..43b6e2e --- /dev/null +++ b/project.clj @@ -0,0 +1,13 @@ +(defproject clout "2.1.2" + :description "A HTTP route matching library" + :url "https://github.com/weavejester/clout" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :dependencies [[org.clojure/clojure "1.5.1"] + [instaparse "1.4.0" :exclusions [org.clojure/clojure]]] + :profiles + {:dev {:jvm-opts ^:replace [] + :dependencies [[ring/ring-mock "0.2.0"] + [criterium "0.4.2"]]} + :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} + :1.7 {:dependencies [[org.clojure/clojure "1.7.0-beta2"]]}}) diff --git a/src/clout/core.clj b/src/clout/core.clj new file mode 100644 index 0000000..afc324b --- /dev/null +++ b/src/clout/core.clj @@ -0,0 +1,126 @@ +(ns clout.core + "A small language for routing." + (:require [clojure.string :as string] + [clojure.set :as set] + [instaparse.core :as insta])) + +(def ^:private re-chars (set "\\.*+|?()[]{}$^")) + +(defn- re-escape [s] + (string/escape s #(if (re-chars %) (str \\ %)))) + +(defn- re-groups* [^java.util.regex.Matcher matcher] + (for [i (range (.groupCount matcher))] + (.group matcher (int (inc i))))) + +(defn- assoc-conj [m k v] + (assoc m k + (if-let [cur (get m k)] + (if (vector? cur) + (conj cur v) + [cur v]) + v))) + +(defn- assoc-keys-with-groups [groups keys] + (reduce (fn [m [k v]] (assoc-conj m k v)) + {} + (map vector keys groups))) + +(defn- request-url [request] + (str (name (:scheme request)) + "://" + (get-in request [:headers "host"]) + (:uri request))) + +(defn- path-info [request] + (or (:path-info request) + (:uri request))) + +(defprotocol Route + (route-matches [route request] + "If the route matches the supplied request, the matched keywords are + returned as a map. Otherwise, nil is returned.")) + +(defrecord CompiledRoute [source re keys absolute?] + Route + (route-matches [_ request] + (let [path-info (if absolute? + (request-url request) + (path-info request)) + matcher (re-matcher re path-info)] + (if (.matches matcher) + (assoc-keys-with-groups (re-groups* matcher) keys)))) + Object + (toString [_] source)) + +(def ^:private route-parser + (insta/parser + "route = (scheme / part) part* + scheme = #'(https?:)?//' + + <part> = literal | escaped | wildcard | param + literal = #'(:[^\\p{L}_*{}\\\\]|[^:*{}\\\\])+' + escaped = #'\\\\.' + wildcard = '*' + + param = key pattern? + key = <':'> #'([\\p{L}_][\\p{L}_0-9-]*)' + pattern = '{' (#'(?:[^{}\\\\]|\\\\.)+' | pattern)* '}'" + :no-slurp true)) + +(defn- parse [parser text] + (let [result (insta/parse parser text)] + (if (insta/failure? result) + (throw (ex-info "Parse error in route string" {:failure result})) + result))) + +(defn- find-route-key [form] + (case (first form) + :wildcard :* + :param (-> form second second keyword))) + +(defn- route-keys [parse-tree] + (->> (rest parse-tree) + (filter (comp #{:param :wildcard} first)) + (map find-route-key))) + +(defn- trim-pattern [pattern] + (some-> pattern (subs 1 (dec (count pattern))))) + +(defn- param-regex [regexs key & [pattern]] + (str "(" (or (trim-pattern pattern) (regexs key) "[^/,;?]+") ")")) + +(defn- route-regex [parse-tree regexs] + (insta/transform + {:route (comp re-pattern str) + :scheme #(if (= % "//") "https?://" %) + :literal re-escape + :escaped #(re-escape (subs % 1)) + :wildcard (constantly "(.*?)") + :param (partial param-regex regexs) + :key keyword + :pattern str} + parse-tree)) + +(defn- absolute-url? [path] + (boolean (re-matches #"(https?:)?//.*" path))) + +(defn route-compile + "Compile a route string for more efficient route matching." + ([path] + (route-compile path {})) + ([path regexs] + (let [ast (parse route-parser path) + ks (route-keys ast)] + (assert (set/subset? (set (keys regexs)) (set ks)) + "unused keys in regular expression map") + (CompiledRoute. + path + (route-regex ast regexs) + (vec ks) + (absolute-url? path))))) + +(extend-type String + Route + (route-matches [route request] + (route-matches (route-compile route) request))) diff --git a/test/clout/core_test.clj b/test/clout/core_test.clj new file mode 100644 index 0000000..8023c54 --- /dev/null +++ b/test/clout/core_test.clj @@ -0,0 +1,127 @@ +(ns clout.core-test + (:import [clojure.lang ExceptionInfo] + [java.util.regex PatternSyntaxException]) + (:require [clojure.test :refer :all] + [ring.mock.request :refer [request]] + [clout.core :refer :all])) + +(deftest fixed-path + (are [path] (route-matches path (request :get path)) + "/" + "/foo" + "/foo/bar" + "/foo/bar.html")) + +(deftest keyword-paths + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/:x" "/foo" {:x "foo"} + "/foo/:x" "/foo/bar" {:x "bar"} + "/a/b/:c" "/a/b/c" {:c "c"} + "/:a/b/:c" "/a/b/c" {:a "a", :c "c"})) + +(deftest keywords-match-extensions + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/foo.:ext" "/foo.txt" {:ext "txt"} + "/:x.:y" "/foo.txt" {:x "foo", :y "txt"})) + +(deftest hyphen-keywords + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/:foo-bar" "/baz" {:foo-bar "baz"} + "/:foo-" "/baz" {:foo- "baz"})) + +(deftest underscore-keywords + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/:foo_bar" "/baz" {:foo_bar "baz"} + "/:_foo" "/baz" {:_foo "baz"})) + +(deftest urlencoded-keywords + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/:x" "/foo%20bar" {:x "foo%20bar"} + "/:x" "/foo+bar" {:x "foo+bar"} + "/:x" "/foo%5Cbar" {:x "foo%5Cbar"})) + +(deftest same-keyword-many-times + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/:x/:x/:x" "/a/b/c" {:x ["a" "b" "c"]} + "/:x/b/:x" "/a/b/c" {:x ["a" "c"]})) + +(deftest non-ascii-keywords + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/:äñßOÔ" "/abc" {:äñßOÔ "abc"} + "/:ÁäñßOÔ" "/abc" {:ÁäñßOÔ "abc"} + "/:ä/:ش" "/foo/bar" {:ä "foo" :ش "bar"} + "/:ä/:ä" "/foo/bar" {:ä ["foo" "bar"]} + "/:Ä-ü" "/baz" {:Ä-ü "baz"} + "/:Ä_ü" "/baz" {:Ä_ü "baz"})) + +(deftest wildcard-paths + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/*" "/foo" {:* "foo"} + "/*" "/foo.txt" {:* "foo.txt"} + "/*" "/foo/bar" {:* "foo/bar"} + "/foo/*" "/foo/bar/baz" {:* "bar/baz"} + "/a/*/d" "/a/b/c/d" {:* "b/c"})) + +(deftest escaped-chars + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/\\:foo" "/foo" nil + "/\\:foo" "/:foo" {})) + +(deftest inline-regexes + (are [path uri params] (= (route-matches path (request :get uri)) params) + "/:x{\\d+}" "/foo" nil + "/:x{\\d+}" "/10" {:x "10"} + "/:x{\\d{2}}" "/2" nil + "/:x{\\d{2}}" "/20" {:x "20"} + "/:x{\\d}/b" "/3/b" {:x "3"} + "/:x{\\d}/b" "/a/b" nil + "/a/:x{\\d}" "/a/4" {:x "4"} + "/a/:x{\\d}" "/a/b" nil)) + +(deftest compiled-routes + (is (= (route-matches (route-compile "/foo/:id") (request :get "/foo/bar")) + {:id "bar"}))) + +(deftest url-paths + (is (route-matches + "http://localhost/" + {:scheme :http + :headers {"host" "localhost"} + :uri "/"})) + (is (route-matches + "//localhost/" + {:scheme :http + :headers {"host" "localhost"} + :uri "/"})) + (is (route-matches + "//localhost/" + {:scheme :https + :headers {"host" "localhost"} + :uri "/"}))) + +(deftest url-port-paths + (let [req (request :get "http://localhost:8080/")] + (is (route-matches "http://localhost:8080/" req)) + (is (not (route-matches "http://localhost:7070/" req))))) + +(deftest unmatched-paths + (is (nil? (route-matches "/foo" (request :get "/bar"))))) + +(deftest path-info-matches + (is (route-matches "/bar" (-> (request :get "/foo/bar") + (assoc :path-info "/bar"))))) + +(deftest custom-matches + (let [route (route-compile "/foo/:bar" {:bar #"\d+"})] + (is (not (route-matches route (request :get "/foo/bar")))) + (is (not (route-matches route (request :get "/foo/1x")))) + (is (route-matches route (request :get "/foo/10"))))) + +(deftest unused-regex-keys + (is (thrown? AssertionError (route-compile "/:foo" {:foa #"\d+"}))) + (is (thrown? AssertionError (route-compile "/:foo" {:foo #"\d+" :bar #".*"})))) + +(deftest invalid-inline-patterns + (is (thrown? ExceptionInfo (route-compile "/:foo{"))) + (is (thrown? ExceptionInfo (route-compile "/:foo{\\d{2}"))) + (is (thrown? PatternSyntaxException (route-compile "/:foo{[a-z}")))) -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/clout-clojure.git _______________________________________________ pkg-java-commits mailing list pkg-java-commits@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits