From 4cdf0607504aca222b2feb760a65e3d7b60f4398 Mon Sep 17 00:00:00 2001 From: gered Date: Sat, 22 Mar 2014 16:57:21 -0400 Subject: [PATCH 01/18] add servlet context path middleware will be used by functions yet-to-be-added --- src/clj_jtwig/web/middleware.clj | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/clj_jtwig/web/middleware.clj diff --git a/src/clj_jtwig/web/middleware.clj b/src/clj_jtwig/web/middleware.clj new file mode 100644 index 0000000..4ca58ee --- /dev/null +++ b/src/clj_jtwig/web/middleware.clj @@ -0,0 +1,12 @@ +(ns clj-jtwig.web.middleware) + +(declare ^:dynamic *servlet-context-path*) + +(defn wrap-servlet-context-path + "Binds the current request's context path to a var which we can use in + various jtwig functions that need it without having to explicitly + pass the path in as a function parameter." + [handler] + (fn [req] + (binding [*servlet-context-path* (:context req)] + (handler req)))) \ No newline at end of file From 32a03b3507850f331b3f8c30e7a043a128612d9a Mon Sep 17 00:00:00 2001 From: gered Date: Sat, 22 Mar 2014 17:09:09 -0400 Subject: [PATCH 02/18] add web-functions --- src/clj_jtwig/web/web_functions.clj | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/clj_jtwig/web/web_functions.clj diff --git a/src/clj_jtwig/web/web_functions.clj b/src/clj_jtwig/web/web_functions.clj new file mode 100644 index 0000000..77d8ecb --- /dev/null +++ b/src/clj_jtwig/web/web_functions.clj @@ -0,0 +1,12 @@ +(ns clj-jtwig.web.web-functions + "web functions, intended to be used by web applications only. most of these will require the + current servlet context path, so use of clj-jtwig.web.middleware.wrap-servlet-context-path + is a prerequisite for these functions." + (:require [clj-jtwig.web.middleware :refer [*servlet-context-path*]])) + +; defined using the same type of map structure as in clj-jtwig.standard-functions + +(defonce web-functions + {"path" + {:fn (fn [url] + (str *servlet-context-path* url))}}) From 57bb442808e6134ad44a3698b20f20ceb27b969d Mon Sep 17 00:00:00 2001 From: gered Date: Sat, 22 Mar 2014 17:09:39 -0400 Subject: [PATCH 03/18] include web-functions in the standard set of repository functions --- src/clj_jtwig/functions.clj | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/clj_jtwig/functions.clj b/src/clj_jtwig/functions.clj index 44a77f7..2f4d235 100644 --- a/src/clj_jtwig/functions.clj +++ b/src/clj_jtwig/functions.clj @@ -4,7 +4,8 @@ (com.lyncode.jtwig.functions.repository DefaultFunctionRepository) (com.lyncode.jtwig.functions.exceptions FunctionNotFoundException FunctionException)) (:require [clj-jtwig.convert :refer [java->clojure clojure->java]]) - (:use [clj-jtwig.standard-functions])) + (:use [clj-jtwig.standard-functions] + [clj-jtwig.web.web-functions])) (defn- make-function-handler [f] (reify JtwigFunction @@ -21,15 +22,18 @@ (aset array index (nth aliases index))) array)) +(defn- add-function-library! [repository functions] + (doseq [[name {:keys [aliases fn]}] functions] + (.add repository + (make-function-handler fn) + name + (make-aliased-array aliases))) + repository) + (defn- create-function-repository [] - (let [repository (new DefaultFunctionRepository (make-array JtwigFunction 0))] - ; always add our standard functions to new repository objects - (doseq [[name {:keys [aliases fn]}] standard-functions] - (.add repository - (make-function-handler fn) - name - (make-aliased-array aliases))) - repository)) + (doto (new DefaultFunctionRepository (make-array JtwigFunction 0)) + (add-function-library! standard-functions) + (add-function-library! web-functions))) ; we'll be reusing the same function repository object for all contexts created when rendering templates. ; any custom functions added will be added to this instance From 1b4fa0fa6f5bdb1034e66af4386156fcad625368 Mon Sep 17 00:00:00 2001 From: gered Date: Sat, 22 Mar 2014 18:54:33 -0400 Subject: [PATCH 04/18] add utils namespace. move common utility functions over --- src/clj_jtwig/core.clj | 20 ++------------------ src/clj_jtwig/utils.clj | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 src/clj_jtwig/utils.clj diff --git a/src/clj_jtwig/core.clj b/src/clj_jtwig/core.clj index 2655bc3..496c043 100644 --- a/src/clj_jtwig/core.clj +++ b/src/clj_jtwig/core.clj @@ -5,7 +5,8 @@ (java.io File FileNotFoundException ByteArrayOutputStream) (java.net URL)) (:require [clojure.walk :refer [stringify-keys]]) - (:use [clj-jtwig.functions])) + (:use [clj-jtwig.functions] + [clj-jtwig.utils])) ; global options (defonce options (atom {; true/false to enable/disable compiled template caching when using templates from @@ -59,17 +60,6 @@ (new JtwigTemplate) (.compile))) -(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/to/file" - (.contains "jar!"))) - -(defn- get-file-last-modified [^File file] - (if (inside-jar? file) - 0 - (.lastModified file))) - (defn- newer? [^File file other-timestamp] (let [file-last-modified (get-file-last-modified file)] ; a time of 0 means the modification time couldn't be read or the file is inside a JAR container. if it's an I/O @@ -129,12 +119,6 @@ [] (reset! compiled-templates {})) -(defn- get-resource-path - (^URL [^String filename] - (-> (Thread/currentThread) - (.getContextClassLoader) - (.getResource filename)))) - (defn- make-model-map [model-map-values {:keys [skip-model-map-stringify?] :as options}] (let [model-map-obj (new JtwigModelMap) values (if-not skip-model-map-stringify? diff --git a/src/clj_jtwig/utils.clj b/src/clj_jtwig/utils.clj new file mode 100644 index 0000000..930e4c7 --- /dev/null +++ b/src/clj_jtwig/utils.clj @@ -0,0 +1,28 @@ +(ns clj-jtwig.utils + "various helper / utility functions" + (: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 From 91b71370bf7aad67c05c01c98c51a30c25fb443c Mon Sep 17 00:00:00 2001 From: gered Date: Sat, 22 Mar 2014 18:55:12 -0400 Subject: [PATCH 05/18] add helper template functions to include CSS and JS files --- src/clj_jtwig/web/web_functions.clj | 35 +++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/clj_jtwig/web/web_functions.clj b/src/clj_jtwig/web/web_functions.clj index 77d8ecb..4797cc5 100644 --- a/src/clj_jtwig/web/web_functions.clj +++ b/src/clj_jtwig/web/web_functions.clj @@ -2,11 +2,42 @@ "web functions, intended to be used by web applications only. most of these will require the current servlet context path, so use of clj-jtwig.web.middleware.wrap-servlet-context-path is a prerequisite for these functions." - (:require [clj-jtwig.web.middleware :refer [*servlet-context-path*]])) + (:import (java.net URI)) + (:require [clj-jtwig.web.middleware :refer [*servlet-context-path*]] + [clojure.string :as str]) + (:use [clj-jtwig.utils])) + +(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-url-string [url] + (if-let [modification-timestamp (if (relative-url? url) + ;; TODO: while 'public' is the default with Compojure, applications can override with something else ... + (->> (str "public" url) + (get-context-url) + (get-resource-modification-date)))] + (str url "?" modification-timestamp) + url)) ; defined using the same type of map structure as in clj-jtwig.standard-functions (defonce web-functions {"path" {:fn (fn [url] - (str *servlet-context-path* url))}}) + (get-context-url url))} + + "stylesheet" + {:fn (fn [url & [media]] + (let [fmt (if media + "" + "")] + (format fmt (get-url-string url) media)))} + + "javascript" + {:fn (fn [url] + (format "" (get-url-string url)))}}) From f68862e7841285a4d1847becc0ea3f9d09edf019 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 09:19:04 -0400 Subject: [PATCH 06/18] extract function to check if file exists this will need to be extended to also check for the existance of files inside of jars --- src/clj_jtwig/core.clj | 2 +- src/clj_jtwig/utils.clj | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/clj_jtwig/core.clj b/src/clj_jtwig/core.clj index 496c043..147095f 100644 --- a/src/clj_jtwig/core.clj +++ b/src/clj_jtwig/core.clj @@ -73,7 +73,7 @@ ; this function really only exists so i can easily change the exception type / message in the future ; since this file-exists check is needed in a few places (defn- err-if-no-file [^File file] - (if-not (.exists file) + (if-not (exists? file) (throw (new FileNotFoundException (str "Template file \"" file "\" not found."))))) (defn- cache-compiled-template! [^File file create-fn] diff --git a/src/clj_jtwig/utils.clj b/src/clj_jtwig/utils.clj index 930e4c7..8fe7e66 100644 --- a/src/clj_jtwig/utils.clj +++ b/src/clj_jtwig/utils.clj @@ -9,6 +9,12 @@ ; 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 exists? [^File file] + (if (inside-jar? file) + ;; TODO: can't use File.exists() for this + true + (.exists file))) + (defn get-file-last-modified [^File file] (if (inside-jar? file) 0 From a84c8588bd9f0b956b261df40d9d987b3cc96497 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 09:43:05 -0400 Subject: [PATCH 07/18] proper method of rendering template files contained in jars --- src/clj_jtwig/core.clj | 13 ++++++++++--- src/clj_jtwig/utils.clj | 6 ++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/clj_jtwig/core.clj b/src/clj_jtwig/core.clj index 147095f..da17e95 100644 --- a/src/clj_jtwig/core.clj +++ b/src/clj_jtwig/core.clj @@ -1,6 +1,7 @@ (ns clj-jtwig.core "wrapper functions for working with JTwig from clojure" (:import (com.lyncode.jtwig JtwigTemplate JtwigContext JtwigModelMap) + (com.lyncode.jtwig.resource ClasspathJtwigResource) (com.lyncode.jtwig.tree.api Content) (java.io File FileNotFoundException ByteArrayOutputStream) (java.net URL)) @@ -56,9 +57,15 @@ (.compile))) (defn- compile-template-file [^File file] - (->> file - (new JtwigTemplate) - (.compile))) + (if (inside-jar? file) + (->> (.getPath file) + (get-jar-resource-filename) + (new ClasspathJtwigResource) + (new JtwigTemplate) + (.compile)) + (->> file + (new JtwigTemplate) + (.compile)))) (defn- newer? [^File file other-timestamp] (let [file-last-modified (get-file-last-modified file)] diff --git a/src/clj_jtwig/utils.clj b/src/clj_jtwig/utils.clj index 8fe7e66..1a3285c 100644 --- a/src/clj_jtwig/utils.clj +++ b/src/clj_jtwig/utils.clj @@ -9,6 +9,12 @@ ; 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-jar-resource-filename [^String resource-filename] + (let [pos (.indexOf resource-filename "jar!")] + (if-not (= -1 pos) + (subs resource-filename (+ pos 5)) + resource-filename))) + (defn exists? [^File file] (if (inside-jar? file) ;; TODO: can't use File.exists() for this From 0358df49c44498607bbf4256a5b02186d1ce9ca8 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 10:06:14 -0400 Subject: [PATCH 08/18] implement checking for existence of files inside jar archives --- src/clj_jtwig/utils.clj | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/clj_jtwig/utils.clj b/src/clj_jtwig/utils.clj index 1a3285c..f9540f5 100644 --- a/src/clj_jtwig/utils.clj +++ b/src/clj_jtwig/utils.clj @@ -1,7 +1,8 @@ (ns clj-jtwig.utils "various helper / utility functions" (:import (java.net URL) - (java.io File))) + (java.io File) + (java.util.jar JarFile))) (defn inside-jar? [^File file] (-> file @@ -15,10 +16,20 @@ (subs resource-filename (+ pos 5)) resource-filename))) +(defn get-jar-filename [^String resource-filename] + (let [start (.indexOf resource-filename "file:") + end (.indexOf resource-filename "jar!")] + (if (and (not= -1 start) + (not= -1 end)) + (subs resource-filename 5 (+ end 3)) + resource-filename))) + (defn exists? [^File file] (if (inside-jar? file) - ;; TODO: can't use File.exists() for this - true + (let [filename (.getPath file) + jar-file (new JarFile (get-jar-filename filename)) + jar-entry (.getJarEntry jar-file (get-jar-resource-filename filename))] + (not (nil? jar-entry))) (.exists file))) (defn get-file-last-modified [^File file] From 2a448b2839d43826190e7feb0003317f95f4c201 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 10:07:00 -0400 Subject: [PATCH 09/18] should be throwing exceptions if resource files not found by default get-resource-path returns nil if the filename doesnt specify a file which isn't in the classpath --- src/clj_jtwig/core.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj_jtwig/core.clj b/src/clj_jtwig/core.clj index da17e95..aa7ee1f 100644 --- a/src/clj_jtwig/core.clj +++ b/src/clj_jtwig/core.clj @@ -168,4 +168,5 @@ the template." [^String filename model-map & [options]] (if-let [resource-filename (get-resource-path filename)] - (render-file (.getPath resource-filename) model-map options))) + (render-file (.getPath resource-filename) model-map options) + (throw (new FileNotFoundException (str "Template file \"" filename "\" not found."))))) From b8c938b2eac79301b52661bc037850bfe7b12583 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 10:28:40 -0400 Subject: [PATCH 10/18] add capitalize_all --- project.clj | 3 ++- src/clj_jtwig/standard_functions.clj | 5 +++++ test/clj_jtwig/functions_test.clj | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 2fd6b6d..70cbca8 100644 --- a/project.clj +++ b/project.clj @@ -6,4 +6,5 @@ :repositories [["sonatype" {:url "http://oss.sonatype.org/content/repositories/releases" :snapshots false}]] :dependencies [[org.clojure/clojure "1.5.1"] - [com.lyncode/jtwig-core "2.1.2"]]) + [com.lyncode/jtwig-core "2.1.2"] + [org.apache.commons/commons-lang3 "3.1"]]) diff --git a/src/clj_jtwig/standard_functions.clj b/src/clj_jtwig/standard_functions.clj index 1817f99..ea92861 100644 --- a/src/clj_jtwig/standard_functions.clj +++ b/src/clj_jtwig/standard_functions.clj @@ -1,6 +1,7 @@ (ns clj-jtwig.standard-functions "standard function definitions. these are functions that are not yet included in JTwig's standard function library and are just here to fill in the gaps for now." + (:import (org.apache.commons.lang3.text WordUtils)) (:use [clojure.pprint])) ; we are using a separate map to hold the standard functions instead of using deftwigfn, etc. because doing it this @@ -23,6 +24,10 @@ (-> sequence vals butlast) (butlast sequence)))} + "capitalize_all" + {:fn (fn [s] + (WordUtils/capitalize s))} + "dump" {:fn (fn [x] (with-out-str diff --git a/test/clj_jtwig/functions_test.clj b/test/clj_jtwig/functions_test.clj index c6a7408..ec1406e 100644 --- a/test/clj_jtwig/functions_test.clj +++ b/test/clj_jtwig/functions_test.clj @@ -284,6 +284,10 @@ (is (= (render "{{ [1, 2, 3, 4, 5]|butlast }}" nil) "[1, 2, 3, 4]"))) + (testing "capitalize_all" + (is (= (render "{{ capitalize_all('hello world') }}" nil) + "Hello World"))) + (testing "dump" (is (= (render "{{ a|dump }}" {:a [{:foo "bar"} [1, 2, 3] "hello"]}) "({\"foo\" \"bar\"} (1 2 3) \"hello\")\n"))) From f316f3553286bf98f974c3229aaba6ecaff2843e Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 11:11:00 -0400 Subject: [PATCH 11/18] add wrap --- src/clj_jtwig/standard_functions.clj | 12 +++++++++++- test/clj_jtwig/functions_test.clj | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/clj_jtwig/standard_functions.clj b/src/clj_jtwig/standard_functions.clj index ea92861..cc084d4 100644 --- a/src/clj_jtwig/standard_functions.clj +++ b/src/clj_jtwig/standard_functions.clj @@ -113,4 +113,14 @@ "sort_descending_by" {:fn (fn [coll k] (sort-by #(get % k) #(compare %2 %1) coll)) - :aliases ["sort_desc_by"]}}) + :aliases ["sort_desc_by"]} + + "wrap" + {:fn (fn [s length & [wrap-long-words? new-line-string]] + (WordUtils/wrap + s + length + new-line-string + (if (nil? wrap-long-words?) + false + wrap-long-words?)))}}) diff --git a/test/clj_jtwig/functions_test.clj b/test/clj_jtwig/functions_test.clj index ec1406e..4ba3573 100644 --- a/test/clj_jtwig/functions_test.clj +++ b/test/clj_jtwig/functions_test.clj @@ -352,4 +352,14 @@ (testing "sort_descending_by" (is (= (render "{{ [{a: 2}, {a: 1}, {a: 5}, {a: 3}, {a: 4}]|sort_descending_by(\"a\") }}" nil) - "[{a=5}, {a=4}, {a=3}, {a=2}, {a=1}]")))) + "[{a=5}, {a=4}, {a=3}, {a=2}, {a=1}]"))) + + (testing "wrap" + (is (= (render "{{ wrap(\"Here is one line of text that is going to be wrapped after 20 columns.\", 20) }}" nil) + "Here is one line of\ntext that is going\nto be wrapped after\n20 columns.")) + (is (= (render "{{ wrap(\"Here is one line of text that is going to be wrapped after 20 columns.\", 20, false, \"
\") }}" nil) + "Here is one line of
text that is going
to be wrapped after
20 columns.")) + (is (= (render "{{ wrap(\"Click here to jump to the commons website - http://commons.apache.org\", 20, false) }}" nil) + "Click here to jump\nto the commons\nwebsite -\nhttp://commons.apache.org")) + (is (= (render "{{ wrap(\"Click here to jump to the commons website - http://commons.apache.org\", 20, true) }}" nil) + "Click here to jump\nto the commons\nwebsite -\nhttp://commons.apach\ne.org")))) From 6b8889752cf79e50c7d3fea430839b23bc78e788 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 11:18:04 -0400 Subject: [PATCH 12/18] add repeat --- src/clj_jtwig/standard_functions.clj | 7 ++++++- test/clj_jtwig/functions_test.clj | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/clj_jtwig/standard_functions.clj b/src/clj_jtwig/standard_functions.clj index cc084d4..062fcab 100644 --- a/src/clj_jtwig/standard_functions.clj +++ b/src/clj_jtwig/standard_functions.clj @@ -1,7 +1,8 @@ (ns clj-jtwig.standard-functions "standard function definitions. these are functions that are not yet included in JTwig's standard function library and are just here to fill in the gaps for now." - (:import (org.apache.commons.lang3.text WordUtils)) + (:import (org.apache.commons.lang3.text WordUtils) + (org.apache.commons.lang3 StringUtils)) (:use [clojure.pprint])) ; we are using a separate map to hold the standard functions instead of using deftwigfn, etc. because doing it this @@ -83,6 +84,10 @@ {:fn (fn [low high & [step]] (range low high (or step 1)))} + "repeat" + {:fn (fn [s n] + (StringUtils/repeat s n))} + "rest" {:fn (fn [sequence] ; matching behaviour of jtwig's first/last implementation diff --git a/test/clj_jtwig/functions_test.clj b/test/clj_jtwig/functions_test.clj index 4ba3573..3241a0b 100644 --- a/test/clj_jtwig/functions_test.clj +++ b/test/clj_jtwig/functions_test.clj @@ -330,6 +330,12 @@ (is (= (render "{{ range(1, 5, 2) }}" nil) "[1, 3]"))) + (testing "repeat" + (is (= (render "{{ repeat('x', 10) }}" nil) + "xxxxxxxxxx")) + (is (= (render "{{ repeat('x', 0) }}" nil) + ""))) + (testing "rest" (is (= (render "{{ [1, 2, 3, 4, 5]|rest }}" nil) "[2, 3, 4, 5]"))) From b6d0f3015de787b98d7901ab0b7dfb97609f2787 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 11:24:21 -0400 Subject: [PATCH 13/18] add pad_left --- src/clj_jtwig/standard_functions.clj | 4 ++++ test/clj_jtwig/functions_test.clj | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/clj_jtwig/standard_functions.clj b/src/clj_jtwig/standard_functions.clj index 062fcab..b894542 100644 --- a/src/clj_jtwig/standard_functions.clj +++ b/src/clj_jtwig/standard_functions.clj @@ -60,6 +60,10 @@ (apply min (first numbers)) (apply min numbers)))} + "pad_left" + {:fn (fn [s size & [padding-string]] + (StringUtils/leftPad s size (or padding-string " ")))} + "random" {:fn (fn [& values] (let [first-value (first values)] diff --git a/test/clj_jtwig/functions_test.clj b/test/clj_jtwig/functions_test.clj index 3241a0b..f2b9587 100644 --- a/test/clj_jtwig/functions_test.clj +++ b/test/clj_jtwig/functions_test.clj @@ -318,6 +318,14 @@ (is (= (render "{{ min(2, 1, 5, 3, 4) }}" nil) "1"))) + (testing "pad_left" + (is (= (render "{{ pad_left('bat', 5) }}" nil) + " bat")) + (is (= (render "{{ pad_left('bat', 3) }}" nil) + "bat")) + (is (= (render "{{ pad_left('bat', 5, 'x') }}" nil) + "xxbat"))) + (testing "random" (is (some #{(render "{{ ['apple', 'orange', 'citrus']|random }}" nil)} ["apple" "orange" "citrus"])) From 04c442a8f8bbe184f3375e0ecf5e1168cddf6a6a Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 11:25:41 -0400 Subject: [PATCH 14/18] add pad_right --- src/clj_jtwig/standard_functions.clj | 4 ++++ test/clj_jtwig/functions_test.clj | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/clj_jtwig/standard_functions.clj b/src/clj_jtwig/standard_functions.clj index b894542..bce8534 100644 --- a/src/clj_jtwig/standard_functions.clj +++ b/src/clj_jtwig/standard_functions.clj @@ -64,6 +64,10 @@ {:fn (fn [s size & [padding-string]] (StringUtils/leftPad s size (or padding-string " ")))} + "pad_right" + {:fn (fn [s size & [padding-string]] + (StringUtils/rightPad s size (or padding-string " ")))} + "random" {:fn (fn [& values] (let [first-value (first values)] diff --git a/test/clj_jtwig/functions_test.clj b/test/clj_jtwig/functions_test.clj index f2b9587..7091303 100644 --- a/test/clj_jtwig/functions_test.clj +++ b/test/clj_jtwig/functions_test.clj @@ -326,6 +326,14 @@ (is (= (render "{{ pad_left('bat', 5, 'x') }}" nil) "xxbat"))) + (testing "pad_right" + (is (= (render "{{ pad_right('bat', 5) }}" nil) + "bat ")) + (is (= (render "{{ pad_right('bat', 3) }}" nil) + "bat")) + (is (= (render "{{ pad_right('bat', 5, 'x') }}" nil) + "batxx"))) + (testing "random" (is (some #{(render "{{ ['apple', 'orange', 'citrus']|random }}" nil)} ["apple" "orange" "citrus"])) From a45bb27479b7e41969654e19dbfc50dc63d55c9a Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 11:27:08 -0400 Subject: [PATCH 15/18] add center --- src/clj_jtwig/standard_functions.clj | 4 ++++ test/clj_jtwig/functions_test.clj | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/clj_jtwig/standard_functions.clj b/src/clj_jtwig/standard_functions.clj index bce8534..4301477 100644 --- a/src/clj_jtwig/standard_functions.clj +++ b/src/clj_jtwig/standard_functions.clj @@ -29,6 +29,10 @@ {:fn (fn [s] (WordUtils/capitalize s))} + "center" + {:fn (fn [s size & [padding-string]] + (StringUtils/center s size (or padding-string " ")))} + "dump" {:fn (fn [x] (with-out-str diff --git a/test/clj_jtwig/functions_test.clj b/test/clj_jtwig/functions_test.clj index 7091303..8295128 100644 --- a/test/clj_jtwig/functions_test.clj +++ b/test/clj_jtwig/functions_test.clj @@ -288,6 +288,14 @@ (is (= (render "{{ capitalize_all('hello world') }}" nil) "Hello World"))) + (testing "center" + (is (= (render "{{ center('bat', 5) }}" nil) + " bat ")) + (is (= (render "{{ center('bat', 3) }}" nil) + "bat")) + (is (= (render "{{ center('bat', 5, 'x') }}" nil) + "xbatx"))) + (testing "dump" (is (= (render "{{ a|dump }}" {:a [{:foo "bar"} [1, 2, 3] "hello"]}) "({\"foo\" \"bar\"} (1 2 3) \"hello\")\n"))) From d807f45091752236e59aa0ebfdc920e9dc9c0fb1 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 11:31:35 -0400 Subject: [PATCH 16/18] add normalize_space --- src/clj_jtwig/standard_functions.clj | 4 ++++ test/clj_jtwig/functions_test.clj | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/clj_jtwig/standard_functions.clj b/src/clj_jtwig/standard_functions.clj index 4301477..3271d97 100644 --- a/src/clj_jtwig/standard_functions.clj +++ b/src/clj_jtwig/standard_functions.clj @@ -64,6 +64,10 @@ (apply min (first numbers)) (apply min numbers)))} + "normalize_space" + {:fn (fn [s] + (StringUtils/normalizeSpace s))} + "pad_left" {:fn (fn [s size & [padding-string]] (StringUtils/leftPad s size (or padding-string " ")))} diff --git a/test/clj_jtwig/functions_test.clj b/test/clj_jtwig/functions_test.clj index 8295128..33f72c8 100644 --- a/test/clj_jtwig/functions_test.clj +++ b/test/clj_jtwig/functions_test.clj @@ -326,6 +326,10 @@ (is (= (render "{{ min(2, 1, 5, 3, 4) }}" nil) "1"))) + (testing "normalize_space" + (is (= (render "{{ normalize_space(' hello world ') }}" nil) + "hello world"))) + (testing "pad_left" (is (= (render "{{ pad_left('bat', 5) }}" nil) " bat")) From fd736455741fe55b9f775f1794222dcb8e8c609b Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 11:32:16 -0400 Subject: [PATCH 17/18] rearrange nth function alphabetically --- src/clj_jtwig/standard_functions.clj | 18 +++++++++--------- test/clj_jtwig/functions_test.clj | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/clj_jtwig/standard_functions.clj b/src/clj_jtwig/standard_functions.clj index 3271d97..11ac30d 100644 --- a/src/clj_jtwig/standard_functions.clj +++ b/src/clj_jtwig/standard_functions.clj @@ -43,15 +43,6 @@ (with-out-str (clojure.pprint/print-table x)))} - "nth" - {:fn (fn [sequence index & optional-not-found] - (let [values (if (map? sequence) ; map instance check to match behaviour of jtwig's first/last implementation - (-> sequence vals) - sequence)] - (if optional-not-found - (nth values index (first optional-not-found)) - (nth values index))))} - "max" {:fn (fn [& numbers] (if (coll? (first numbers)) @@ -68,6 +59,15 @@ {:fn (fn [s] (StringUtils/normalizeSpace s))} + "nth" + {:fn (fn [sequence index & optional-not-found] + (let [values (if (map? sequence) ; map instance check to match behaviour of jtwig's first/last implementation + (-> sequence vals) + sequence)] + (if optional-not-found + (nth values index (first optional-not-found)) + (nth values index))))} + "pad_left" {:fn (fn [s size & [padding-string]] (StringUtils/leftPad s size (or padding-string " ")))} diff --git a/test/clj_jtwig/functions_test.clj b/test/clj_jtwig/functions_test.clj index 33f72c8..730d0b2 100644 --- a/test/clj_jtwig/functions_test.clj +++ b/test/clj_jtwig/functions_test.clj @@ -304,16 +304,6 @@ (is (= (render "{{ t|dump_table }}", {:t [{:a 1 :b 2 :c 3} {:b 5 :a 7 :c "dog"}]}) "\n| b | c | a |\n|---+-----+---|\n| 2 | 3 | 1 |\n| 5 | dog | 7 |\n"))) - (testing "nth" - (is (= (render "{{ [1, 2, 3, 4, 5]|nth(2) }}" nil) - "3")) - (is (thrown-with-msg? - Exception - #"java.lang.IndexOutOfBoundsException" - (render "{{ [1, 2, 3, 4, 5]|nth(6) }}" nil))) - (is (= (render "{{ [1, 2, 3, 4, 5]|nth(6, \"not found\") }}" nil) - "not found"))) - (testing "max" (is (= (render "{{ [2, 1, 5, 3, 4]|max }}" nil) "5")) @@ -330,6 +320,16 @@ (is (= (render "{{ normalize_space(' hello world ') }}" nil) "hello world"))) + (testing "nth" + (is (= (render "{{ [1, 2, 3, 4, 5]|nth(2) }}" nil) + "3")) + (is (thrown-with-msg? + Exception + #"java.lang.IndexOutOfBoundsException" + (render "{{ [1, 2, 3, 4, 5]|nth(6) }}" nil))) + (is (= (render "{{ [1, 2, 3, 4, 5]|nth(6, \"not found\") }}" nil) + "not found"))) + (testing "pad_left" (is (= (render "{{ pad_left('bat', 5) }}" nil) " bat")) From aae62834d81030ca82484f989c7fc1229dfd4437 Mon Sep 17 00:00:00 2001 From: gered Date: Sun, 23 Mar 2014 11:45:47 -0400 Subject: [PATCH 18/18] bump version --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 70cbca8..0157225 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject clj-jtwig "0.2.2" +(defproject clj-jtwig "0.3" :description "Clojure wrapper for JTwig" :url "https://github.com/gered/clj-jtwig" :license {:name "Apache License, Version 2.0"