Permalink
Browse files

Initial refactor towards 0.1.

  • Loading branch information...
1 parent 24fe773 commit cbeebac281a156f111ee02a6b2908de45387c81b @mmcgrana committed Jul 11, 2009
View
2 .gitignore
@@ -1 +1 @@
-bundle
+bundle
View
85 README.textile
@@ -1,51 +1,68 @@
h1. Ring
-Ring is a Clojure web applications library inspired by Python's WSGI and Ruby's Rack. By abstracting the details of HTTP into a simple, unified API, Ring allows web applications to be constructed of modular components that can be shared among a variety of applications, web servers, and web frameworks.
+Ring is a Clojure web applications library inspired by Python's WSGI and Ruby's
+Rack. By abstracting the details of HTTP into a simple, unified API, Ring allows
+web applications to be constructed of modular components that can be shared among
+a variety of applications, web servers, and web frameworks.
-The @SPEC@ file at the root of this distribution for provides a complete description of the Ring interface.
+The @SPEC@ file at the root of this distribution for provides a complete
+description of the Ring interface.
-h2. Quick Setup
+h2. Quick Start
-First clone the Ring source:
+First clone the Ring source and download Ring's dependencies:
git clone git://github.com/mmcgrana/ring.git
- cd ring
-
-Then download the needed dependencies:
-
+ cd ring
ant deps
-The @deps@ ant target downloads all Ring dependencies and unpacks them to @deps/@. To see a live "Hello World" Ring app, run:
+To see a live "Hello World" Ring app, run:
java -Djava.ext.dirs=deps clojure.main src/ring/examples/hello_world.clj
-then visit @http://localhost:8080/@ in your browser; the Ring app will respond to your request with a simple HTML page indicating the time of day.
+Now visit @http://localhost:8080/@ in your browser; the Ring app will respond
+to your request with a simple HTML page indicating the time of day.
To see a more sophisticated Ring app, run:
- java -Djava.ext.dirs=deps clojure.main src/ring/examples/wrapping.clj
-
- * If you request @http://localhost:8080/@ in your browser the @ring.dump@ handler will respond with an HTML page representing the request map that it received (see the @SPEC@ for details on the request map).
- * If you request @http://localhost:8080/clojure.png@, the @ring.file@ middleware will detect that there is a @clojure.png@ file in the app's @public@ directory and return that image as a response.
- * If you request @http://localhost:8080/error@, the app will produce an error that will be caught by the @ring.show-exceptions@ middleware, which will in turn return a readable backtrace as the HTML response.
+ java -Djava.ext.dirs=deps clojure.main src/ring/example/wrapping.clj
+
+ * If you request @http://localhost:8080/@ in your browser the @ring.dump@
+ handler will respond with an HTML page representing the request map that it
+ received (see the @SPEC@ for details on the request map).
+ * If you request @http://localhost:8080/clojure.png@, the @ring.file@
+ middleware will detect that there is a @clojure.png@ file in the app's
+ @public@ directory and return that image as a response.
+ * If you request @http://localhost:8080/error@, the app will produce an error
+ that will be caught by the @ring.show-exceptions@ middleware, which will in
+ turn return a readable stacktrace as the HTML response.
-h2. All Available Libs
-
- * @ring.jetty@: Adapter for the Jetty webserver.
- * @ring.httpcore@: Adapter for the Apache HttpCore webserver.
- * @ring.file@: Middleware that serves static files out of a public directory.
- * @ring.file-info@: Middleware that augments response headers with info about File responses.
- * @ring.static@: Middleware that serves static files with specified prefixes out of a public directory.
- * @ring.dump@: Handler that dumps request maps as HTML responses for debugging.
- * @ring.show-exceptions@: Middleware that catches exceptions and displays readable backtraces for debugging.
- * @ring.reload@: Middleware to automatically reload selected libs before each requests, minimizing server restarts.
- * @ring.builder@: Helpers for composing Ring apps from multiple handlers and middleware.
- * @ring.lint@: Linter for the Ring interface, ensures compliance with the Ring spec.
- * @ring.examples.*@: Various example Ring apps.
+h2. Included Libs
+
+ * @ring.adapter.jetty@: Adapter for the Jetty webserver.
+ * @ring.adapter.httpcore@: Adapter for the Apache HttpCore webserver.
+ * @ring.middleware.file@: Middleware that serves static files out of a public
+ directory.
+ * @ring.middleware.file-info@: Middleware that augments response headers with
+ info about File responses.
+ * @ring.middleware.static@: Middleware that serves static files with specified
+ prefixes out of a public directory.
+ * @ring.middleware.show-exceptions@: Middleware that catches exceptions and
+ displays readable stacktraces for debugging.
+ * @ring.middleware.reload@: Middleware to automatically reload selected libs
+ before each requests, minimizing server restarts.
+ * @ring.handler.dump@: Handler that dumps request maps as HTML responses for
+ debugging.
+ * @ring.util.builder@: Helpers for composing Ring apps from multiple handlers
+ and middleware.
+ * @ring.util.lint@: Linter for the Ring interface, ensures compliance with the
+ Ring spec.
+ * @ring.example.*@: Various example Ring apps.
h2. Development
-Ring is being actively developed; you can track its progress and contribute at the project's "GitHub"::http://github.com/mmcgrana/ring page.
+Ring is being actively developed; you can track its progress and contribute at
+the project's "GitHub"::http://github.com/mmcgrana/ring page.
To run all the Ring unit tests:
@@ -57,21 +74,13 @@ You can learn more about Ring's dependencies at the following sites:
* @jetty-6.1.14.jar@, @jetty-util-6.1.14.jar@, @servlet-api-2.5-6.1.14.jar@: "mortbay.org/jetty":http://www.mortbay.org/jetty
* @httpcore-4.0.jar@, @httpcore-nio-4.0.jar@: "hc.apache.org":http://hc.apache.org/httpcomponents-core/index.html
* @commons-io-1.4.jar@: "commons.apache.org/io":http://commons.apache.org/io/
- * @clj-html.jar@, @clj-html-helpers.jar@, @clj-backtrace.jar@, @clj-unit@ (testing): "github.com/mmcgrana":http://github.com/mmcgrana
+ * @clj-html.jar@, @clj-html-helpers.jar@, @clj-stacktrace.jar@, @clj-unit@: "github.com/mmcgrana":http://github.com/mmcgrana
h2. Thanks
This project borrows heavily from Ruby's Rack and Python's WSGI, and I thank the communities developing and supporting those projects.
---
-<pre><code>
- +--->HTTP Request--->[Adapter]--->Req1---->[Middleware]--->Req2----+
- | |
- [Client] [Handler]
- | |
- +----HTTP Response<--[Adapter]<---Resp2<---[Middleware]<---Resp1<--+
-</code></pre>
-
Copyright (c) 2009 Mark McGranaghan and released under an MIT license.
Clojure logo by Tom Hickey.
View
6 SPEC
@@ -22,9 +22,9 @@ Handlers are run via Ring adapters, which are in turn responsible for
implementing the HTTP protocol and abstracting the handlers that they run from
the details of the protocol.
-Adapters are implemented as functions of two arguments: an options map and a
-handler. The options map provides any needed configuration to the adapter, such
-as the port on which to run.
+Adapters are implemented as functions of two arguments: a handler and an options
+map. The options map provides any needed configuration to the adapter, such as
+the port on which to run.
Once initialized, adapters receive HTTP requests, parse them to construct an
request map, and then invoke their handler with this request map as an
View
59 src/ring/httpcore.clj → src/ring/adapter/httpcore.clj
@@ -1,16 +1,16 @@
-(ns ring.httpcore
- (:import (org.apache.http HttpRequest Header HttpEntity HttpEntityEnclosingRequest HttpResponse
+(ns ring.adapter.httpcore
+ (:import (org.apache.http HttpRequest Header HttpEntityHttpEntityEnclosingRequest HttpResponse
ConnectionClosedException HttpException HttpServerConnection)
(org.apache.http.entity AbstractHttpEntity StringEntity EntityTemplate InputStreamEntity
FileEntity ContentProducer)
(org.apache.http.message BasicHeader BasicHeaderElement)
(org.apache.http.params BasicHttpParams CoreConnectionPNames CoreProtocolPNames)
- (org.apache.http.protocol HttpRequestHandler BasicHttpContext HttpService BasicHttpProcessor
+ (org.apache.http.protocol HttpRequestHandler BasicHttpContext HttpService BasicHttpProcessor
ResponseDate ResponseServer ResponseContent ResponseConnControl HttpRequestHandlerRegistry
HttpContext)
- (org.apache.http.impl DefaultConnectionReuseStrategy DefaultHttpResponseFactory
+ (org.apache.http.impl DefaultConnectionReuseStrategy DefaultHttpResponseFactory
DefaultHttpServerConnection)
- (java.io File FileInputStream InputStream OutputStream OutputStreamWriter IOException
+ (java.io File FileInputStream InputStream OutputStream OutputStreamWriter IOException
InterruptedIOException)
(java.net URI ServerSocket)
(java.util.concurrent Executors Executor ThreadFactory))
@@ -20,9 +20,9 @@
([form] form)
([form next-form & forms]
`(when-let [x# ~form] (-?> (-> x# ~next-form) ~@forms))))
-
+
(defmacro #^{:private true} instance?-> [type x & forms]
- `(when (instance? ~type ~x) (-> ~(vary-meta x assoc :tag type) ~@forms)))
+ `(when (instance? ~type ~x) (-> ~(vary-meta x assoc :tag type) ~@forms)))
(defn- charset [#^BasicHeader content-type-header]
(-?> content-type-header .getElements #^BasicHeaderElement first (.getParameterByName "charset") .getValue))
@@ -31,20 +31,20 @@
(.toLowerCase s java.util.Locale/ENGLISH))
(defn- build-req-map
- "Augments the given request-prototype (a map) to represent the given HTTP
+ "Augments the given request-prototype (a map) to represent the given HTTP
request, to be passed as the Ring request to an app."
[#^HttpRequest request request-prototype]
- (let [request-line (.getRequestLine request)
+ (let [request-line (.getRequestLine request)
headers (reduce
(fn [header-map #^Header header]
(assoc header-map
(-> header .getName lower-case)
(.getValue header)))
{} (seq (.getAllHeaders request)))
- host (or (headers "host")
+ host (or (headers "host")
(str (request-prototype :server-name) ":" (request-prototype :server-port 80)))
uri (URI. (str "http://" host (.getUri request-line)))]
- (into (or request-prototype {})
+ (into (or request-prototype {})
{:server-port (.getPort uri)
:server-name (.getHost uri)
:uri (.getRawPath uri)
@@ -53,7 +53,7 @@
:headers headers
:content-type (headers "content-type")
:content-length (when-let [len (instance?-> HttpEntityEnclosingRequest request .getEntity .getContentLength)]
- (when (>= len 0) len))
+ (when (>= len 0) len))
:character-encoding (instance?-> HttpEntityEnclosingRequest request .getEntity .getContentEncoding charset)
:body (instance?-> HttpEntityEnclosingRequest request .getEntity .getContent)})))
@@ -73,13 +73,13 @@
(when body
(let [content-type (headers "Content-Type")
charset (when content-type (charset (BasicHeader. "Content-Type" content-type)))
- content-length (headers "Content-Length")
+ content-length (headers "Content-Length")
entity
(cond
- (string? body)
+ (string? body)
(StringEntity. body)
(seq? body)
- (EntityTemplate.
+ (EntityTemplate.
(proxy [ContentProducer] []
(writeTo [#^OutputStream s]
(let [w (if charset (OutputStreamWriter. s #^String charset) (OutputStreamWriter. s))]
@@ -97,7 +97,7 @@
(.setEntity response entity))))
(defn- ring-handler
- "Returns an Handler implementation for the given app.
+ "Returns an Handler implementation for the given app.
The HttpContext must contains a map associated to \"ring.request-prototype\"."
[app]
(proxy [HttpRequestHandler] []
@@ -110,7 +110,7 @@
;; Simple HTTP Server
(defn- handle-request
- "Handle the request, usually called from a worker thread."
+ "Handle the request, usually called from a worker thread."
[#^HttpService httpservice #^HttpServerConnection conn request-prototype]
(let [context (doto (BasicHttpContext. nil)
(.setAttribute "ring.request-prototype" request-prototype))]
@@ -135,31 +135,31 @@
(.addInterceptor (ResponseConnControl.)))
registry (doto (HttpRequestHandlerRegistry.)
(.register "*" (ring-handler app)))]
- (doto (HttpService. httpproc
+ (doto (HttpService. httpproc
(DefaultConnectionReuseStrategy.)
(DefaultHttpResponseFactory.))
(.setParams params)
(.setHandlerResolver registry))))
-(defn executor-execute
+(defn executor-execute
"Executes (apply f args) using the specified Executor."
[#^java.util.concurrent.Executor executor f & args]
(.execute executor #(apply f args)))
-(defn run
+(defn run-httpcore
"Serve the given app according to the options.
Options:
- :port, an Integer,
- :server-name, a String -- for old HTTP/1.0 clients,
- :server-port, an Integer -- for old HTTP/1.0 clients, when public facing port is different from :port,
- :execute, a function (whose sig is [f & args]) that applies f to args -- usually in another thread."
- [{:keys [port server-name server-port execute]} app]
- (let [execute (or execute (partial executor-execute
+ :port, an Integer.
+ :server-name, a String (for old HTTP/1.0 clients).
+ :server-port, an Integer (for old HTTP/1.0 clients, when public facing port is different from :port).
+ :execute, a function with signature [f & args] that applies f to args, usually in another thread."
+ [app {:keys [port server-name server-port execute]}]
+ (let [execute (or execute (partial executor-execute
(Executors/newCachedThreadPool
- (proxy [ThreadFactory] []
+ (proxy [ThreadFactory] []
(newThread [r]
(doto (Thread. #^Runnable r)
- (.setDaemon true)))))))
+ (.setDaemon true)))))))
params (doto (BasicHttpParams.)
(.setIntParameter CoreConnectionPNames/SO_TIMEOUT 5000)
(.setIntParameter CoreConnectionPNames/SOCKET_BUFFER_SIZE (* 8 1024))
@@ -172,6 +172,5 @@
(let [socket (.accept serversocket)
conn (doto (DefaultHttpServerConnection.)
(.bind socket params))]
- (execute handle-request httpservice conn
+ (execute handle-request httpservice conn
(assoc request-prototype :remote-addr (-> socket .getInetAddress .getHostAddress))))))))
-
View
6 src/ring/jetty.clj → src/ring/adapter/jetty.clj
@@ -1,4 +1,4 @@
-(ns ring.jetty
+(ns ring.adapter.jetty
(:import (javax.servlet.http HttpServletRequest HttpServletResponse)
(org.mortbay.jetty.handler AbstractHandler)
(org.mortbay.jetty Server)
@@ -81,11 +81,11 @@
(apply-resp-map response resp)
(.setHandled request true)))))
-(defn run
+(defn run-jetty
"Serve the given app according to the options.
Options:
:port, an Integer."
- [options app]
+ [app options]
(let [port (or (:port options) (throwf ":port missing from options"))
server (doto (Server. port) (.setSendDateHeader true))
handler (proxy-handler app)]
View
8 src/ring/builder.clj
@@ -1,8 +0,0 @@
-(ns ring.builder)
-
-(defn wrap-if
- "If test is logically true, returns the result of invoking the wrapper on the
- core app, i.e. a wrapped app; if test is logically false, returns the core
- app."
- [test wrapper-fn core-app]
- (if test (wrapper-fn core-app) core-app))
View
8 src/ring/examples/hello_world.clj → src/ring/example/hello_world.clj
@@ -1,7 +1,7 @@
; A very simple Ring application.
-(ns ring.examples.hello-world
- (:require ring.jetty)
+(ns ring.example.hello-world
+ (:use ring.adapter.jetty)
(:import java.util.Date java.text.SimpleDateFormat))
(def formatter (SimpleDateFormat. "HH:mm:ss"));
@@ -11,7 +11,7 @@
{:status 200
:headers {"Content-Type" "text/html"}
:body (str "<h3>Hello World from Ring</h3>"
- "<p>The current time is "
+ "<p>The current time is "
(.format formatter (Date.)) ".</p>")})
-(ring.jetty/run {:port 8080} app)
+(run-jetty app {:port 8080})
View
21 src/ring/example/linted.clj
@@ -0,0 +1,21 @@
+; An example of inserting the linter between each component to ensure
+; compliance to the Ring spec.
+
+(ns ring.examples.linted
+ (:use (ring.handler dump)
+ (ring.middleware stacktrace file file-info reload lint)
+ (ring.adapter jetty)))
+
+(def app
+ (-> handle-dump
+ wrap-lint
+ wrap-stacktrace
+ wrap-lint
+ wrap-file-info
+ wrap-lint
+ (wrap-file "src/ring/examples/public")
+ wrap-lint
+ (wrap-reload '(ring.dump)
+ wrap-lint)))
+
+(run-jetty app {:port 8080})
View
0 src/ring/examples/public/clojure.png → src/ring/example/public/clojure.png
File renamed without changes
View
22 src/ring/example/wrapping.clj
@@ -0,0 +1,22 @@
+; A example of modular construction of Ring apps.
+
+(ns ring.example.wrapping
+ (:use (ring.handler dump)
+ (ring.middleware stacktrace file-info file)
+ (ring.adapter jetty)
+ (clojure.contrib fcase)))
+
+(defn wrap-error [app]
+ (fn [req]
+ (if (= "/error" (:uri req))
+ (throwf "Demonstrating ring.middleware.stacktrace")
+ (app req))))
+
+(def app
+ (-> handle-dump
+ wrap-stacktrace
+ wrap-file-info
+ (wrap-file "src/ring/examples/public")
+ wrap-error))
+
+(run-jetty app {:port 8080})
View
19 src/ring/examples/linted.clj
@@ -1,19 +0,0 @@
-; An example of inserting the linter inserted each component to ensure
-; compliance to the Ring spec.
-
-(ns ring.examples.linted
- (:require (ring backtrace file file-info reload lint dump jetty))
- (:import (java.io File)))
-
-(def app
- (ring.lint/wrap
- (ring.backtrace/wrap
- (ring.lint/wrap
- (ring.file-info/wrap
- (ring.lint/wrap
- (ring.file/wrap (File. "src/ring/examples/public")
- (ring.lint/wrap
- (ring.reload/wrap '(ring.dump)
- ring.dump/app)))))))))
-
-(ring.jetty/run {:port 8080} app)
View
16 src/ring/examples/wrapping.clj
@@ -1,16 +0,0 @@
-; A example of modular construction of Ring apps.
-
-(ns ring.examples.wrapping
- (:require (ring backtrace file-info file dump jetty))
- (:import (java.io File)))
-
-(def app
- (ring.backtrace/wrap
- (ring.file-info/wrap
- (ring.file/wrap (File. "src/ring/examples/public")
- (fn [req]
- (if (= "/error" (:uri req))
- (throw (Exception. "Demonstrating ring.backtrace"))
- (ring.dump/app req)))))))
-
-(ring.jetty/run {:port 8080} app)
View
47 src/ring/file.clj
@@ -1,47 +0,0 @@
-(ns ring.file
- (:use clojure.contrib.except
- ring.utils)
- (:import (java.io File)))
-
-(defn- ensure-dir
- "Ensures that the given directory exists."
- [dir]
- (throw-if-not (.exists dir)
- "Directory does not exist: %s" dir))
-
-(defn- forbidden
- []
- {:status 403
- :headers {"Content-Type" "text/html"}
- :body "<html><body><h1>403 Forbidden</h1></body></html>"})
-
-(defn- success
- [file]
- {:status 200 :headers {} :body file})
-
-(defn- maybe-file
- "Returns the File corresponding to the given relative path within the given
- dir if it exists, or nil if no such file exists."
- [dir path]
- (let [file (File. dir path)]
- (and (.exists file) (.canRead file) file)))
-
-(defn wrap
- "Wrap an app such that a given directory is checked for a static file
- with which to respond to the request, proxying the request to the
- wrapped app if such a file does not exist."
- [dir app]
- (ensure-dir dir)
- (fn [req]
- (if (#{:get :head} (:request-method req))
- (let [uri (url-decode (:uri req))]
- (if (str-includes? ".." uri)
- (forbidden)
- (let [path (cond
- (.endsWith "/" uri) (str uri "index.html")
- (re-match? #"\.[a-z]+$" uri) uri
- :else (str uri ".html"))]
- (if-let [file (maybe-file dir path)]
- (success file)
- (app req)))))
- (app req))))
View
8 src/ring/dump.clj → src/ring/handler/dump.clj
@@ -1,7 +1,7 @@
-(ns ring.dump
+(ns ring.handler.dump
(:use (clj-html core utils helpers)
- clojure.contrib.def
- clojure.set)
+ (clojure.contrib def)
+ (clojure set))
(:import (org.apache.commons.io IOUtils)))
(declare css)
@@ -39,7 +39,7 @@
(domap-str [key (sort user-keys)]
(req-pair key req))]]]))]]])
-(defn app
+(defn handle-dump
"Returns a response tuple corresponding to an HTML dump of the request
req as it was recieved by this app."
[req]
View
60 src/ring/middleware/file.clj
@@ -0,0 +1,60 @@
+(ns ring.middleware.file
+ (:use clojure.contrib.except)
+ (:import (java.io File)))
+
+(defn- url-decode
+ "Returns the form-url-decoded version of the given string."
+ [encoded]
+ (java.net.URLDecoder/decode encoded "UTF-8"))
+
+(defn- str-includes?
+ "Returns logical truth iff the given target appears in the given string"
+ [target string]
+ (<= 0 (.indexOf string target)))
+
+(defn- ensure-dir
+ "Ensures that the given directory exists and returns it, throwing if it does
+ not. If the directory is given as a String, it is coerced to a Fil before
+ returning."
+ [dir]
+ (let [fdir (if (string? dir) (File. dir) dir)]
+ (throw-if-not (.exists fdir)
+ "Directory does not exist: %s" fdir)
+ fdir))
+
+(defn- forbidden
+ []
+ {:status 403
+ :headers {"Content-Type" "text/html"}
+ :body "<html><body><h1>403 Forbidden</h1></body></html>"})
+
+(defn- success
+ [file]
+ {:status 200 :headers {} :body file})
+
+(defn- maybe-file
+ "Returns the File corresponding to the given relative path within the given
+ dir if it exists, or nil if no such file exists."
+ [dir path]
+ (let [file (File. dir path)]
+ (and (.exists file) (.canRead file) file)))
+
+(defn wrap-file
+ "Wrap an app such that a given directory is checked for a static file
+ with which to respond to the request, proxying the request to the
+ wrapped app if such a file does not exist."
+ [app dir]
+ (let [fdir (ensure-dir dir)]
+ (fn [req]
+ (if (#{:get :head} (:request-method req))
+ (let [uri (url-decode (:uri req))]
+ (if (str-includes? ".." uri)
+ (forbidden)
+ (let [path (cond
+ (.endsWith "/" uri) (str uri "index.html")
+ (re-find #"\.[a-z]+$" uri) uri
+ :else (str uri ".html"))]
+ (if-let [file (maybe-file fdir path)]
+ (success file)
+ (app req)))))
+ (app req)))))
View
34 src/ring/file_info.clj → src/ring/middleware/file_info.clj
@@ -1,7 +1,7 @@
-(ns ring.file-info
- (:use clojure.contrib.def)
- (:import org.apache.commons.io.FilenameUtils
- java.io.File))
+(ns ring.middleware.file-info
+ (:use (clojure.contrib def))
+ (:import (org.apache.commons.io FilenameUtils)
+ (java.io File)))
(defvar- base-mime-types
{"ai" "application/postscript"
@@ -66,20 +66,18 @@
(get mime-types (FilenameUtils/getExtension (.getPath file))
"application/octet-stream"))
-(defn wrap
+(defn wrap-file-info
"Wrap an app such that responses with a file a body will have
corresponding Content-Type and Content-Length headers added if they are not
allready present and can be determined from the file. If two arguments are
- given the first is taken to be a map of file extensions to content types
- that will supplement the default, built-in map."
- ([custom-mime-types app]
- (let [mime-types (merge base-mime-types custom-mime-types)]
- (fn [req]
- (let [{:keys [headers body] :as response} (app req)]
- (if (instance? File body)
- (assoc response :headers
- (assoc headers "Content-Length" (str (.length body))
- "Content-Type" (guess-mime-type body mime-types)))
- response)))))
- ([app]
- (wrap {} app)))
+ given, the second is taken to be a map of file extensions to content types
+ that will supplement the default, built-in map."
+ [app & [custom-mime-types]]
+ (let [mime-types (merge base-mime-types custom-mime-types)]
+ (fn [req]
+ (let [{:keys [headers body] :as response} (app req)]
+ (if (instance? File body)
+ (assoc response :headers
+ (assoc headers "Content-Length" (str (.length body))
+ "Content-Type" (guess-mime-type body mime-types)))
+ response)))))
View
7 src/ring/lint.clj → src/ring/middleware/lint.clj
@@ -1,5 +1,6 @@
-(ns ring.lint
- (:use clojure.set clojure.contrib.except)
+(ns ring.middleware.lint
+ (:use (clojure set)
+ (clojure.contrib except))
(:import (java.io File InputStream)))
(defn lint
@@ -97,7 +98,7 @@
(lint-namespacing resp "response"
#{:status :headers :body}))
-(defn wrap
+(defn wrap-lint
"Wrap an app to validate incoming requests and outgoing responses
according to the Ring spec."
[app]
View
6 src/ring/reload.clj → src/ring/middleware/reload.clj
@@ -1,12 +1,12 @@
-(ns ring.reload)
+(ns ring.middleware.reload)
-(defn wrap
+(defn wrap-reload
"Wrap an app such that before a request is passed to the app, each namespace
identified by syms in reloadables is reloaded.
Currently this requires that the namespaces in question are being (re)loaded
from un-jarred source files, as apposed to source files in jars or compiled
classes."
- [reloadables app]
+ [app reloadables]
(fn [req]
(doseq [ns-sym reloadables]
(require ns-sym :reload))
View
11 src/ring/backtrace.clj → src/ring/middleware/stacktrace.clj
@@ -1,8 +1,7 @@
-(ns ring.backtrace
+(ns ring.middleware.stacktrace
(:use (clj-html core utils helpers)
- clojure.contrib.str-utils
- (clj-backtrace core repl)
- ring.utils))
+ (clojure.contrib str-utils)
+ (clj-stacktrace core repl)))
(declare css)
@@ -43,11 +42,11 @@
Currently supports delegation to either js or html exception views."
[req e]
(let [accept (get-in req [:headers "accept"])]
- (if (and accept (re-match? #"^text/javascript" accept))
+ (if (and accept (re-find #"^text/javascript" accept))
(js-response req e)
(html-reponse req e))))
-(defn wrap
+(defn wrap-stacktrace
"Wrap an app such that exceptions thrown within the wrapped app are caught
and a helpful debugging response is returned."
[app]
View
17 src/ring/middleware/static.clj
@@ -0,0 +1,17 @@
+(ns ring.middleware.static
+ (:use (ring.middleware file)))
+
+(defn wrap-static
+ "Like ring.file, but takes an additional statics, a coll of Strings that will
+ be used to test incoming requests uris. If a uri begins with any of the
+ strings in the statics coll, the middleware will check to see if a file can be
+ served from the public-dir before proxying back to the given app; if the uri
+ does not correspond to one of these strings, the middleware proxies the
+ request directly back to the app without touching the filesystem."
+ [app public-dir statics]
+ (let [app-with-file (wrap-file app public-dir)]
+ (fn [req]
+ (let [uri (:uri req)]
+ (if (some #(.startsWith uri %) statics)
+ (app-with-file req)
+ (app req))))))
View
18 src/ring/static.clj
@@ -1,18 +0,0 @@
-(ns ring.static
- (:require ring.file)
- (:use ring.utils))
-
-(defn wrap
- "Like ring.file, but takes an additional statics, a coll of Strings that will
- be used to test incoming requests uris. If the uri begins with any of the
- strings in statics, the middleware will check to see if a files can be served
- from the public-dir before proxying back to the given app; if the uri does not
- match the re, proxies the request directly back to the app without touching the
- filesystem."
- [public-dir statics app]
- (let [app-with-file (ring.file/wrap public-dir app)]
- (fn [req]
- (let [uri (:uri req)]
- (if (some #(.startsWith uri %) statics)
- (app-with-file req)
- (app req))))))
View
25 src/ring/utils.clj
@@ -1,25 +0,0 @@
-(ns ring.utils
- (:use clojure.contrib.str-utils))
-
-(defn url-decode
- "Returns the form-url-decoded version of the given string."
- [encoded]
- (java.net.URLDecoder/decode encoded "UTF-8"))
-
-(defn str-includes?
- "Returns logical truth iff the given target appears in the given string"
- [target string]
- (<= 0 (.indexOf string target)))
-
-(defn re-match?
- "Returns true iff the given string contains a match for the given pattern."
- [#^java.util.regex.Pattern pattern string]
- (.find (.matcher pattern string)))
-
-(defn re-get
- "Returns the nth captured group resulting from matching the given pattern
- against the given string, or nil if no match is found."
- [re n s]
- (let [m (re-matcher re s)]
- (if (.find m)
- (.group m n))))
View
10 test/ring/builder_test.clj
@@ -1,10 +0,0 @@
-(ns ring.builder-test
- (:use clj-unit.core ring.builder))
-
-(deftest "wrap-if"
- (let [core-app inc
- wrapper-fn (fn [app] (fn [req] (+ 2 (app req))))
- unwrapped (wrap-if false wrapper-fn core-app)
- wrapped (wrap-if true wrapper-fn core-app)]
- (assert= 1 (unwrapped 0))
- (assert= 3 (wrapped 0))))
View
35 test/ring/file_info_test.clj
@@ -1,35 +0,0 @@
-(ns ring.file-info-test
- (:use clj-unit.core ring.file-info)
- (:import java.io.File))
-
-(def non-file-app (wrap (constantly {:headers {} :body "body"})))
-
-(def known-file (File. "test/ring/assets/plain.txt"))
-(def known-file-app (wrap (constantly {:headers {} :body known-file})))
-
-(def unknown-file (File. "test/ring/assets/random.xyz"))
-(def unknown-file-app (wrap (constantly {:headers {} :body unknown-file})))
-
-(def custom-type-app (wrap {"txt" "custom/type"}
- (constantly {:headers {} :body known-file})))
-
-(deftest "wrap: non-file response"
- (assert= {:headers {} :body "body"} (non-file-app {})))
-
-(deftest "wrap: known file response"
- (assert=
- {:headers {"Content-Type" "text/plain" "Content-Length" "6"}
- :body known-file}
- (known-file-app {})))
-
-(deftest "wrap: unknown file resonse"
- (assert=
- {:headers {"Content-Type" "application/octet-stream" "Content-Length" "7"}
- :body unknown-file}
- (unknown-file-app {})))
-
-(deftest "wrap: custom mime types"
- (assert=
- {:headers {"Content-Type" "custom/type" "Content-Length" "6"}
- :body known-file}
- (custom-type-app {})))
View
13 test/ring/dump_test.clj → test/ring/handler/dump_test.clj
@@ -1,6 +1,7 @@
-(ns ring.dump-test
- (:use clj-unit.core ring.dump)
- (:import java.io.ByteArrayInputStream))
+(ns ring.handler.dump-test
+ (:use (clj-unit core)
+ (ring.handler dump))
+ (:import (java.io ByteArrayInputStream)))
(def post-req
{:uri "/foo/bar"
@@ -11,8 +12,8 @@
{:uri "/foo/bar"
:request-method :get})
-(deftest "app"
- (let [{:keys [status]} (app post-req)]
+(deftest "handle-dump"
+ (let [{:keys [status]} (handle-dump post-req)]
(assert= 200 status))
- (let [{:keys [status]} (app get-req)]
+ (let [{:keys [status]} (handle-dump get-req)]
(assert= 200 status)))
View
38 test/ring/middleware/file_info_test.clj
@@ -0,0 +1,38 @@
+(ns ring.middleware.file-info-test
+ (:use (clj-unit core)
+ (ring.middleware file-info))
+ (:import (java.io File)))
+
+(def non-file-app (wrap-file-info (constantly {:headers {} :body "body"})))
+
+(def known-file (File. "test/ring/assets/plain.txt"))
+(def known-file-app (wrap-file-info (constantly {:headers {} :body known-file})))
+
+(def unknown-file (File. "test/ring/assets/random.xyz"))
+(def unknown-file-app (wrap-file-info (constantly {:headers {} :body unknown-file})))
+
+(def custom-type-app
+ (wrap-file-info
+ (constantly {:headers {} :body known-file})
+ {"txt" "custom/type"}))
+
+(deftest "wrap-file-info: non-file response"
+ (assert= {:headers {} :body "body"} (non-file-app {})))
+
+(deftest "wrap-file-info: known file response"
+ (assert=
+ {:headers {"Content-Type" "text/plain" "Content-Length" "6"}
+ :body known-file}
+ (known-file-app {})))
+
+(deftest "wrap-file-info: unknown file resonse"
+ (assert=
+ {:headers {"Content-Type" "application/octet-stream" "Content-Length" "7"}
+ :body unknown-file}
+ (unknown-file-app {})))
+
+(deftest "wrap-file-info: custom mime types"
+ (assert=
+ {:headers {"Content-Type" "custom/type" "Content-Length" "6"}
+ :body known-file}
+ (custom-type-app {})))
View
27 test/ring/file_test.clj → test/ring/middleware/file_test.clj
@@ -1,42 +1,43 @@
-(ns ring.file-test
- (:use clj-unit.core ring.file)
- (:import java.io.File))
+(ns ring.middleware.file-test
+ (:use (clj-unit core)
+ (ring.middleware file))
+ (:import (java.io File)))
-(deftest "wrap: no directory"
+(deftest "wrap-file: no directory"
(assert-throws #"Directory does not exist"
- (wrap (File. "not_here") (constantly :response))))
+ (wrap-file (constantly :response) (File. "not_here"))))
-(def public-dir (File. "test/ring/assets"))
+(def public-dir "test/ring/assets")
(def index-html (File. public-dir "index.html"))
(def foo-html (File. public-dir "foo.html"))
-(def app (wrap public-dir (constantly :response)))
+(def app (wrap-file (constantly :response) public-dir))
-(deftest "wrap: unsafe method"
+(deftest "wrap-file: unsafe method"
(assert= :response (app {:request-method :post :uri "/foo"})))
-(deftest "wrap: forbidden url"
+(deftest "wrap-file: forbidden url"
(let [{:keys [status body]} (app {:request-method :get :uri "/../foo"})]
(assert= 403 status)
(assert-match #"Forbidden" body)))
-(deftest "wrap: directory"
+(deftest "wrap-file: directory"
(let [{:keys [status headers body]} (app {:request-method :get :uri "/"})]
(assert= 200 status)
(assert= {} headers)
(assert= index-html body)))
-(deftest "wrap: file without extension"
+(deftest "wrap-file: file without extension"
(let [{:keys [status headers body]} (app {:request-method :get :uri "/foo"})]
(assert= 200 status)
(assert= {} headers)
(assert= foo-html body)))
-(deftest "wrap: file with extension"
+(deftest "wrap-file: file with extension"
(let [{:keys [status headers body]} (app {:request-method :get :uri "/foo.html"})]
(assert= 200 status)
(assert= {} headers)
(assert= foo-html body)))
-(deftest "wrap: no file"
+(deftest "wrap-file: no file"
(assert= :response (app {:request-method :get :uri "/dynamic"})))
View
10 test/ring/lint_test.clj → test/ring/middleware/lint_test.clj
@@ -1,5 +1,6 @@
-(ns ring.lint-test
- (:use clj-unit.core ring.lint)
+(ns ring.middleware.lint-test
+ (:use (clj-unit core)
+ (ring.middleware lint))
(:import (java.io File InputStream ByteArrayInputStream)))
(defn str-input-stream
@@ -25,12 +26,11 @@
{:status 200 :headers {} :body "valid"})
(def valid-response-app
- (wrap (fn [req] valid-response)))
+ (wrap-lint (fn [req] valid-response)))
(defn constant-app
[constant-response]
- (wrap (fn [req] constant-response)))
-
+ (wrap-lint (fn [req] constant-response)))
(defmacro lints-req
[key goods bads]
View
9 test/ring/middleware/reload_test.clj
@@ -0,0 +1,9 @@
+(ns ring.middleware.reload-test
+ (:use (clj-unit core)
+ (ring.middleware reload)))
+
+(def app
+ (wrap-reload (constantly :response) '(ring.middleware.reload)))
+
+(deftest "wrap-reload"
+ (assert= :response (app :request)))
View
9 test/ring/backtrace_test.clj → test/ring/middleware/stacktrace_test.clj
@@ -1,12 +1,13 @@
-(ns ring.backtrace-test
- (:use clj-unit.core ring.backtrace))
+(ns ring.middleware.stacktrace-test
+ (:use (clj-unit core)
+ (ring.middleware stacktrace)))
-(def app (wrap #(throw (Exception. "fail"))))
+(def app (wrap-stacktrace #(throw (Exception. "fail"))))
(def html-req {})
(def js-req {:headers {"accept" "text/javascript"}})
-(deftest "wrap"
+(deftest "wrap-stacktrace"
(let [{:keys [status headers] :as response} (app html-req)]
(assert= 500 status)
(assert= {"Content-Type" "text/html"} headers))
View
11 test/ring/static_test.clj → test/ring/middleware/static_test.clj
@@ -1,18 +1,19 @@
-(ns ring.static-test
- (:use clj-unit.core ring.static)
- (:import java.io.File))
+(ns ring.middleware.static-test
+ (:use (clj-unit core)
+ (ring.middleware static))
+ (:import (java.io File)))
(def public-dir (File. "test/ring/assets"))
(def foo-html (File. "test/ring/assets/foo.html"))
(def nested-foo-html (File. "test/ring/assets/bars/foo.html"))
(def statics ["/foo.html" "/bars/"])
-(def app (ring.static/wrap public-dir statics (constantly {:body :dynamic})))
+(def app (wrap-static (constantly {:body :dynamic}) public-dir statics))
(defn app-response-body [uri]
(:body (app {:request-method :get :uri uri})))
-(deftest "wrap"
+(deftest "wrap-static"
(assert= foo-html (app-response-body "/foo.html"))
(assert= nested-foo-html (app-response-body "/bars/foo.html"))
(assert= :dynamic (app-response-body "/not/static")))
View
8 test/ring/reload_test.clj
@@ -1,8 +0,0 @@
-(ns ring.reload-test
- (:use clj-unit.core ring.reload))
-
-(def app (constantly :response))
-
-(deftest "wrap"
- (let [wrapped (wrap '(ring.reload) app)]
- (assert= :response (wrapped :request))))
View
15 test/run.clj
@@ -1,10 +1,9 @@
(use 'clj-unit.core)
(require-and-run-tests
- 'ring.builder-test
- 'ring.dump-test
- 'ring.lint-test
- 'ring.file-test
- 'ring.file-info-test
- 'ring.static-test
- 'ring.reload-test
- 'ring.backtrace-test)
+ 'ring.handler.dump-test
+ 'ring.middleware.lint-test
+ 'ring.middleware.file-test
+ 'ring.middleware.file-info-test
+ 'ring.middleware.static-test
+ 'ring.middleware.reload-test
+ 'ring.middleware.stacktrace-test)

0 comments on commit cbeebac

Please sign in to comment.