diff --git a/src/clj_pebble/core.clj b/src/clj_pebble/core.clj index 944d783..48edddb 100644 --- a/src/clj_pebble/core.clj +++ b/src/clj_pebble/core.clj @@ -8,6 +8,7 @@ (:require [clojure.walk :refer [stringify-keys]] [clj-pebble.extensions :as ext] [clj-pebble.standard-extensions :as std] + [clj-pebble.web.extensions :as web] [clj-pebble.options :refer [options]])) (defonce classpath-loader (ClasspathLoader.)) @@ -29,7 +30,8 @@ (defn- make-pebble-engine [] (let [engine (-> (PebbleEngine. classpath-loader) - (ext/add-extensions-library! std/extensions))] + (ext/add-extensions-library! std/extensions) + (ext/add-extensions-library! web/extensions))] (apply-options! engine) engine)) diff --git a/src/clj_pebble/utils.clj b/src/clj_pebble/utils.clj new file mode 100644 index 0000000..6d983ad --- /dev/null +++ b/src/clj_pebble/utils.clj @@ -0,0 +1,27 @@ +(ns clj-pebble.utils + (:import (java.net URL) + (java.io File))) + +(defn inside-jar? [^File file] + (-> file + (.getPath) + ; the path of a file inside a jar looks something like "jar:file:/path/to/file.jar!/path/inside/jar/to/file" + (.contains "jar!"))) + +(defn get-file-last-modified [^File file] + (if (inside-jar? file) + 0 + (.lastModified file))) + +(defn get-resource-path + (^URL [^String filename] + (-> (Thread/currentThread) + (.getContextClassLoader) + (.getResource filename)))) + +(defn get-resource-modification-date [^String filename] + (when-let [resource-filename (get-resource-path filename)] + (->> resource-filename + (.getPath) + (new File) + (get-file-last-modified)))) \ No newline at end of file diff --git a/src/clj_pebble/web/extensions.clj b/src/clj_pebble/web/extensions.clj new file mode 100644 index 0000000..3975002 --- /dev/null +++ b/src/clj_pebble/web/extensions.clj @@ -0,0 +1,80 @@ +(ns clj-pebble.web.extensions + (:import (java.net URI)) + (:require [clojure.string :as str] + [clj-pebble.web.middleware :refer [*servlet-context-path*]] + [clj-pebble.options :refer [options]]) + (:use [clj-pebble.utils])) + +;; TODO: while 'public' is the default with Compojure, applications can override with something else ... +;; should make this customizable (some option added to clj-pebble.options likely ...) +(def root-resource-path "public") + +(defn- get-context-url [url] + (str *servlet-context-path* url)) + +(defn- relative-url? [url] + (if-not (str/blank? url) + (let [uri (new URI url)] + (str/blank? (.getScheme uri))))) + +(defn- get-resource-modification-timestamp [^String resource-url] + (if (relative-url? resource-url) + (->> (str root-resource-path resource-url) + (get-context-url) + (get-resource-modification-date)))) + +(defn- get-url-string [url] + (if-let [modification-timestamp (get-resource-modification-timestamp url)] + ; because it looks kind of dumb to have '?0' at the end of URLs when running from a jar ... + (if (= modification-timestamp 0) + url + (str url "?" modification-timestamp)) + url)) + +(defn- minified-url? [url] + (re-matches #"^(.+\.)min\.(css|js)$" url)) + +(defn- make-minified-url [^String url] + (let [pos (.lastIndexOf url (int \.))] + (if (> pos -1) + (let [name (subs url 0 pos) + extension (subs url (inc pos))] + (str name ".min." extension)) + url))) + +(defn- get-minified-resource-url [url] + (if (or (not (:check-for-minified-web-resources @options)) + (minified-url? url)) + url + (let [minified-url (make-minified-url url)] + (if (get-resource-path (str root-resource-path minified-url)) + minified-url + url)))) + +; defined using the same type of map structure as in clj-pebble.standard-extensions + +(defonce extensions + {:functions + {"path" + {:fn (fn [url] + (get-context-url url))} + + "stylesheet" + {:fn (fn [url & [media]] + (let [fmt (if media + "" + "") + resource-path (get-minified-resource-url url)] + (format fmt (get-url-string resource-path) media)))} + + "javascript" + {:fn (fn [url] + (let [fmt "" + resource-path (get-minified-resource-url url)] + (format fmt (get-url-string resource-path))))}} + + :filters + {} + + :tests + {}}) \ No newline at end of file diff --git a/src/clj_pebble/web/middleware.clj b/src/clj_pebble/web/middleware.clj new file mode 100644 index 0000000..5b6f733 --- /dev/null +++ b/src/clj_pebble/web/middleware.clj @@ -0,0 +1,8 @@ +(ns clj-pebble.web.middleware) + +(declare ^:dynamic *servlet-context-path*) + +(defn wrap-servlet-context-path [handler] + (fn [req] + (binding [*servlet-context-path* (:context req)] + (handler req)))) \ No newline at end of file