Browse files

Split the overly large cascade namespace into three: cascade, cascade…

….request, and cascade.asset
  • Loading branch information...
1 parent f90d32b commit afd094664be13930ba48722e0c3553cbba4b2880 @hlship committed Nov 4, 2011
Showing with 202 additions and 155 deletions.
  1. +5 −0 NOTICE.txt
  2. +1 −152 src/main/clojure/cascade.clj
  3. +91 −0 src/main/clojure/cascade/asset.clj
  4. +99 −0 src/main/clojure/cascade/request.clj
  5. +6 −3 src/test/main.clj
View
5 NOTICE.txt
@@ -0,0 +1,5 @@
+This project includes jQuery 1.7, dual licensed under the MIT or GPL Version 2 licenses.
+http://jquery.org/
+
+This project includes RequireJS 1.0.1, dual licensed under new BSD, and MIT licenses.
+http://requirejs.org/
View
153 src/main/clojure/cascade.clj
@@ -15,15 +15,7 @@
(ns
cascade
"Core functions and macros used when implementing Cascade views"
- (:import
- [java.net URL]
- [java.io File FileInputStream BufferedInputStream InputStream]
- [java.util Calendar Date])
- (:require
- [ring.util [response :as ring] [mime-type :as mime-type]]
- [ring.middleware.file-info :as file-info])
(:use
- [compojure core]
[cascade dom]
[cascade.internal viewbuilder parse-functions]))
@@ -65,150 +57,7 @@ preceding the parameters vector. The function's forms are an implicit inline blo
[comment]
(comment-node comment))
-; This may change into a value passed as a key in a filter, or perhaps a rebindable var.
-(def asset-configuration
- "The atom containing the asset configuration, needed to create Asset and handle asset requests.
-This should only be changed at startup, by (initialize-assets)."
- (atom nil))
-
-(defprotocol Asset
- "Represent a server-side resource so that it can be exposed efficiently to the client."
- (^String file-name [asset] "Returns just the name of the Asset.")
- (^InputStream content-stream [asset] "Returns the content of the Asset as a stream of bytes, or null if the Asset does not exist.")
- (^String client-url [asset] "Returns an absolute URL to the Asset."))
-
-(deftype FileAsset [file url]
- Asset
- (file-name [asset] (.getName file))
- (content-stream [asset]
- (if (.canRead file)
- (-> (FileInputStream. file) BufferedInputStream.)))
- (client-url [asset] url)
-
- ; Each "implementation" of Asset should also extend ToAttributeValueString
- ; There isn't a proper way to express this in Clojure, though I think there
- ; should be.
-
- ToAttributeValueString
- (to-attribute-value-string [asset] url))
-
-(defn build-client-url
- "Builds a complete client URL for an asset.
-domain
- Keyword for the asset domain (either :file or :classpath, for the built-in domains)
-path
- Path within the domain to the asset, consisting of a series of names seperated by slashes (but not starting with one)."
- [domain path]
- (str (:assets-folder @asset-configuration) "/" (name domain) "/" path))
-
-(defn file-asset
- "Creates an Asset representing a file in the application's public folder, as configured in (initialize-assets)."
- [path]
- (let [file-path (str (:public-folder @asset-configuration) "/" path)
- file (File. file-path)
- client-url (build-client-url :file path)]
- (->FileAsset file client-url)))
-
-(deftype ClasspathAsset [name ^URL resource-url url]
- Asset
- (file-name [asset] name)
- (content-stream [asset]
- (and resource-url (-> (.openStream resource-url) BufferedInputStream.)))
- (client-url [asset] url)
-
- ToAttributeValueString
- (to-attribute-value-string [asset] url))
-
-(defn classpath-asset
- "Create an Asset representing a file on the classpath."
- [path]
- (let [file-name (last (.split path "/"))
- resource-url (-> (Thread/currentThread) .getContextClassLoader (.getResource path))
- client-url (build-client-url :classpath path)]
- (->ClasspathAsset file-name resource-url client-url)))
-
-(defn get-content-type [asset]
- (let [name (file-name asset)
- dotx (.lastIndexOf name ".")
- extension (.substring name (inc dotx))
- content-type (-> @asset-configuration :file-extensions (get extension))]
- (or content-type "text/plain")))
-
-(defn wrap-exception-handling
- "Middleware for standard Cascade exception reporting; exceptions are caught and reported using the Cascade
-exception report view."
- [handler]
- ; Just a placeholder for now
- handler)
-
-(defn- now-plus-ten-years
- "Returns a string representation of the current Date plus ten years, used for setting expiration date of assets."
- []
- (let [format (file-info/make-http-format)]
- (->>
- (doto (Calendar/getInstance)
- (.add Calendar/YEAR 10))
- .getTime
- (.format format))))
-
-(defn asset-handler
- "Internal handler for asset requests. Returns the response from the handler, or nil if
-no handler matches the name.
-asset-factories
- Maps keywords for the asset domain (eg., :file or :classpath) to the factory function (which takes a path)
-domain
- Keyword used to locate factory function
-path
- String path passed to the handler."
- [asset-factories domain path]
- (let [factory (domain asset-factories)
- asset (and factory (factory path))
- stream (and asset (content-stream asset))]
- (if stream
- (->
- (ring/response stream)
- (ring/header "Expires" (@asset-configuration :expiration))
- (ring/content-type (get-content-type asset))))))
-
-(defn initialize-assets
- "Initializes asset handling for Cascade. This sets an application version (a value incorporated into URLs, which
-should change with each new deployment. Named arguments:
-:virtual-folder (default \"assets\")
- The root folder under which assets will be exposed to the client.
-:public-folder (default \"public\")
- The file system folder under which file assets are stored. May be an absolute path, should not end with a slash.
-:file-extensions
- Additional file-extension to MIME type mappings, beyond the default set (defined by ring.util.mime-type/default-mime-types).
-:asset-factories
- Additional asset dispatcher mappings. Keys are domain keywords, values are functions that accept a path within that domain.
-The functions should construct and return a cascade/Asset."
- [application-version & {:keys [virtual-folder public-folder file-extensions asset-factories]
- :or {virtual-folder "assets"
- public-folder "public"}}]
- (let [root (str "/" virtual-folder "/" application-version)
- asset-factories (merge {:file file-asset
- :classpath classpath-asset} asset-factories)]
- (reset! asset-configuration
- {:application-version application-version
- :expiration (now-plus-ten-years)
- :public-folder public-folder
- :assets-folder root
- :file-extensions (merge mime-type/default-mime-types file-extensions)})
- (printf "Initialized asset access at virtual folder %s\n" root)
- (wrap-exception-handling
- (GET [(str root "/:domain/:path") :path #".*"]
- [domain path] (asset-handler asset-factories (keyword domain) path)))))
-
-(defn wrap-html
- "Ring middleware that wraps a handler so that the return value from the handler (a seq of DOM nodes)
-is serialized to HTML (as lazy seq of strings)."
- [handler]
- (fn [req]
- (->
- (handler req)
- serialize-html)))
-
(defview stylesheet
- "Creates a <link> element for a stylesheet. The resource should be either a String or an Asset."
+ "Creates a <link> element for a stylesheet. The resource should be either a String or a cascade.asset/Asset."
[resource]
:link {:rel :stylesheet :type "text/css" :href resource})
View
91 src/main/clojure/cascade/asset.clj
@@ -0,0 +1,91 @@
+; Copyright 2011 Howard M. Lewis Ship
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+; implied. See the License for the specific language governing permissions
+; and limitations under the License.
+;
+
+(ns cascade.asset
+ "Defines basic types of assets (resources exposed to the client browser)."
+ (:use
+ [cascade dom])
+ (:import
+ [java.io File FileInputStream BufferedInputStream InputStream]
+ [java.net URL]))
+
+; This may change into a value passed as a key in a filter, or perhaps a rebindable var.
+(def asset-configuration
+ "The atom containing the asset configuration, needed to create Asset and handle asset requests.
+This should only be changed at startup, by (cascade.request/initialize)."
+ (atom nil))
+
+(defprotocol Asset
+ "Represent a server-side resource so that it can be exposed efficiently to the client."
+ (^String file-name [asset] "Returns just the name of the Asset.")
+ (^InputStream content-stream [asset] "Returns the content of the Asset as a stream of bytes, or null if the Asset does not exist.")
+ (^String client-url [asset] "Returns an absolute URL to the Asset."))
+
+(deftype FileAsset [file url]
+ Asset
+ (file-name [asset] (.getName file))
+ (content-stream [asset]
+ (if (.canRead file)
+ (-> (FileInputStream. file) BufferedInputStream.)))
+ (client-url [asset] url)
+
+ ; Each "implementation" of Asset should also extend ToAttributeValueString
+ ; There isn't a proper way to express this in Clojure, though I think there
+ ; should be.
+
+ ToAttributeValueString
+ (to-attribute-value-string [asset] url))
+
+(defn build-client-url
+ "Builds a complete client URL for an asset.
+domain
+ Keyword for the asset domain (either :file or :classpath, for the built-in domains)
+path
+ Path within the domain to the asset, consisting of a series of names seperated by slashes (but not starting with one)."
+ [domain path]
+ (str (:assets-folder @asset-configuration) "/" (name domain) "/" path))
+
+(defn file-asset
+ "Creates an Asset representing a file in the application's public folder, as configured in (initialize)."
+ [path]
+ (let [file-path (str (:public-folder @asset-configuration) "/" path)
+ file (File. file-path)
+ client-url (build-client-url :file path)]
+ (->FileAsset file client-url)))
+
+(deftype ClasspathAsset [name ^URL resource-url url]
+ Asset
+ (file-name [asset] name)
+ (content-stream [asset]
+ (and resource-url (-> (.openStream resource-url) BufferedInputStream.)))
+ (client-url [asset] url)
+
+ ToAttributeValueString
+ (to-attribute-value-string [asset] url))
+
+(defn classpath-asset
+ "Create an Asset representing a file on the classpath."
+ [path]
+ (let [file-name (last (.split path "/"))
+ resource-url (-> (Thread/currentThread) .getContextClassLoader (.getResource path))
+ client-url (build-client-url :classpath path)]
+ (->ClasspathAsset file-name resource-url client-url)))
+
+(defn get-content-type [asset]
+ (let [name (file-name asset)
+ dotx (.lastIndexOf name ".")
+ extension (.substring name (inc dotx))
+ content-type (-> @asset-configuration :file-extensions (get extension))]
+ (or content-type "text/plain")))
View
99 src/main/clojure/cascade/request.clj
@@ -0,0 +1,99 @@
+; Copyright 2011 Howard M. Lewis Ship
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+; implied. See the License for the specific language governing permissions
+; and limitations under the License.
+
+(ns cascade.request
+ "Functions used to initialize a Cascade application and process requests."
+ (:import
+ [java.util Calendar Date])
+ (:require
+ [ring.util [response :as ring] [mime-type :as mime-type]]
+ [ring.middleware.file-info :as file-info])
+ (:use
+ [compojure core]
+ [cascade dom asset]))
+
+(defn wrap-exception-handling
+ "Middleware for standard Cascade exception reporting; exceptions are caught and reported using the Cascade
+exception report view. *NOT YET IMPLEMENTED*"
+ [handler]
+ ; Just a placeholder for now
+ handler)
+
+(defn asset-handler
+ "Internal handler for asset requests. Returns the response from the handler, or nil if
+no handler matches the name.
+asset-factories
+ Maps keywords for the asset domain (eg., :file or :classpath) to the factory function (which takes a path)
+domain
+ Keyword used to locate factory function
+path
+ String path passed to the handler."
+ [asset-factories domain path]
+ (let [factory (domain asset-factories)
+ asset (and factory (factory path))
+ stream (and asset (content-stream asset))]
+ (if stream
+ (->
+ (ring/response stream)
+ (ring/header "Expires" (@asset-configuration :expiration))
+ (ring/content-type (get-content-type asset))))))
+
+
+(defn- now-plus-ten-years
+ "Returns a string representation of the current Date plus ten years, used for setting expiration date of assets."
+ []
+ (let [format (file-info/make-http-format)]
+ (->>
+ (doto (Calendar/getInstance)
+ (.add Calendar/YEAR 10))
+ .getTime
+ (.format format))))
+
+(defn initialize
+ "Initializes asset handling for Cascade. This sets an application version (a value incorporated into URLs, which
+should change with each new deployment. Named arguments:
+:virtual-folder (default \"assets\")
+ The root folder under which assets will be exposed to the client.
+:public-folder (default \"public\")
+ The file system folder under which file assets are stored. May be an absolute path, should not end with a slash.
+:file-extensions
+ Additional file-extension to MIME type mappings, beyond the default set (defined by ring.util.mime-type/default-mime-types).
+:asset-factories
+ Additional asset dispatcher mappings. Keys are domain keywords, values are functions that accept a path within that domain.
+The functions should construct and return a cascade/Asset."
+ [application-version & {:keys [virtual-folder public-folder file-extensions asset-factories]
+ :or {virtual-folder "assets"
+ public-folder "public"}}]
+ (let [root (str "/" virtual-folder "/" application-version)
+ asset-factories (merge {:file file-asset
+ :classpath classpath-asset} asset-factories)]
+ (reset! asset-configuration
+ {:application-version application-version
+ :expiration (now-plus-ten-years)
+ :public-folder public-folder
+ :assets-folder root
+ :file-extensions (merge mime-type/default-mime-types file-extensions)})
+ (printf "Initialized asset access at virtual folder %s\n" root)
+ (wrap-exception-handling
+ (GET [(str root "/:domain/:path") :path #".*"]
+ [domain path] (asset-handler asset-factories (keyword domain) path)))))
+
+(defn wrap-html
+ "Ring middleware that wraps a handler so that the return value from the handler (a seq of DOM nodes)
+is serialized to HTML (as lazy seq of strings)."
+ [handler]
+ (fn [req]
+ (->
+ (handler req)
+ serialize-html)))
View
9 src/test/main.clj
@@ -1,6 +1,7 @@
(ns main
- (:use compojure.core cascade ring.adapter.jetty)
+ (:use compojure.core cascade cascade.asset ring.adapter.jetty)
(:require
+ [cascade.request :as cr]
[compojure.route :as route]
[compojure.handler :as handler]))
@@ -24,9 +25,11 @@
]])
(defroutes main-routes
- (GET "/hello" [] (wrap-html hello-world))
+ ; Temporary: eventually we'll pass a couple of routes
+ ;; into cr/initialize
+ (GET "/hello" [] (cr/wrap-html hello-world))
; Executes with src/test as the current directory
- (initialize-assets "1.0" :public-folder "webapp")
+ (cr/initialize "1.0" :public-folder "webapp")
(route/not-found "Cascade Demo: No such resource"))
(def app

0 comments on commit afd0946

Please sign in to comment.