Merge branch 'master' into java6
Conflicts: project.clj
This commit is contained in:
commit
b66cdbf79e
|
@ -1,7 +1,8 @@
|
||||||
(defproject clj-jtwig-java6 "0.2.2"
|
(defproject clj-jtwig-java6 "0.3"
|
||||||
:description "Clojure wrapper for JTwig (Java 6 dependencies)"
|
:description "Clojure wrapper for JTwig (Java 6 dependencies)"
|
||||||
:url "https://github.com/gered/clj-jtwig/tree/java6"
|
:url "https://github.com/gered/clj-jtwig/tree/java6"
|
||||||
:license {:name "Apache License, Version 2.0"
|
:license {:name "Apache License, Version 2.0"
|
||||||
:url "http://www.apache.org/licenses/LICENSE-2.0"}
|
:url "http://www.apache.org/licenses/LICENSE-2.0"}
|
||||||
:dependencies [[org.clojure/clojure "1.5.1"]
|
:dependencies [[org.clojure/clojure "1.5.1"]
|
||||||
[com.lyncode/jtwig-core-java6 "2.1.2"]])
|
[com.lyncode/jtwig-core-java6 "2.1.2"]
|
||||||
|
[org.apache.commons/commons-lang3 "3.1"]])
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
(ns clj-jtwig.core
|
(ns clj-jtwig.core
|
||||||
"wrapper functions for working with JTwig from clojure"
|
"wrapper functions for working with JTwig from clojure"
|
||||||
(:import (com.lyncode.jtwig JtwigTemplate JtwigContext JtwigModelMap)
|
(:import (com.lyncode.jtwig JtwigTemplate JtwigContext JtwigModelMap)
|
||||||
|
(com.lyncode.jtwig.resource ClasspathJtwigResource)
|
||||||
(com.lyncode.jtwig.tree.api Content)
|
(com.lyncode.jtwig.tree.api Content)
|
||||||
(java.io File FileNotFoundException ByteArrayOutputStream)
|
(java.io File FileNotFoundException ByteArrayOutputStream)
|
||||||
(java.net URL))
|
(java.net URL))
|
||||||
(:require [clojure.walk :refer [stringify-keys]])
|
(:require [clojure.walk :refer [stringify-keys]])
|
||||||
(:use [clj-jtwig.functions]))
|
(:use [clj-jtwig.functions]
|
||||||
|
[clj-jtwig.utils]))
|
||||||
|
|
||||||
; global options
|
; global options
|
||||||
(defonce options (atom {; true/false to enable/disable compiled template caching when using templates from
|
(defonce options (atom {; true/false to enable/disable compiled template caching when using templates from
|
||||||
|
@ -55,20 +57,15 @@
|
||||||
(.compile)))
|
(.compile)))
|
||||||
|
|
||||||
(defn- compile-template-file [^File file]
|
(defn- compile-template-file [^File file]
|
||||||
(->> file
|
|
||||||
(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)
|
(if (inside-jar? file)
|
||||||
0
|
(->> (.getPath file)
|
||||||
(.lastModified file)))
|
(get-jar-resource-filename)
|
||||||
|
(new ClasspathJtwigResource)
|
||||||
|
(new JtwigTemplate)
|
||||||
|
(.compile))
|
||||||
|
(->> file
|
||||||
|
(new JtwigTemplate)
|
||||||
|
(.compile))))
|
||||||
|
|
||||||
(defn- newer? [^File file other-timestamp]
|
(defn- newer? [^File file other-timestamp]
|
||||||
(let [file-last-modified (get-file-last-modified file)]
|
(let [file-last-modified (get-file-last-modified file)]
|
||||||
|
@ -83,7 +80,7 @@
|
||||||
; this function really only exists so i can easily change the exception type / message in the future
|
; 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
|
; since this file-exists check is needed in a few places
|
||||||
(defn- err-if-no-file [^File file]
|
(defn- err-if-no-file [^File file]
|
||||||
(if-not (.exists file)
|
(if-not (exists? file)
|
||||||
(throw (new FileNotFoundException (str "Template file \"" file "\" not found.")))))
|
(throw (new FileNotFoundException (str "Template file \"" file "\" not found.")))))
|
||||||
|
|
||||||
(defn- cache-compiled-template! [^File file create-fn]
|
(defn- cache-compiled-template! [^File file create-fn]
|
||||||
|
@ -129,12 +126,6 @@
|
||||||
[]
|
[]
|
||||||
(reset! compiled-templates {}))
|
(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}]
|
(defn- make-model-map [model-map-values {:keys [skip-model-map-stringify?] :as options}]
|
||||||
(let [model-map-obj (new JtwigModelMap)
|
(let [model-map-obj (new JtwigModelMap)
|
||||||
values (if-not skip-model-map-stringify?
|
values (if-not skip-model-map-stringify?
|
||||||
|
@ -177,4 +168,5 @@
|
||||||
the template."
|
the template."
|
||||||
[^String filename model-map & [options]]
|
[^String filename model-map & [options]]
|
||||||
(if-let [resource-filename (get-resource-path filename)]
|
(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.")))))
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
(com.lyncode.jtwig.functions.repository DefaultFunctionRepository)
|
(com.lyncode.jtwig.functions.repository DefaultFunctionRepository)
|
||||||
(com.lyncode.jtwig.functions.exceptions FunctionNotFoundException FunctionException))
|
(com.lyncode.jtwig.functions.exceptions FunctionNotFoundException FunctionException))
|
||||||
(:require [clj-jtwig.convert :refer [java->clojure clojure->java]])
|
(: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]
|
(defn- make-function-handler [f]
|
||||||
(reify JtwigFunction
|
(reify JtwigFunction
|
||||||
|
@ -21,15 +22,18 @@
|
||||||
(aset array index (nth aliases index)))
|
(aset array index (nth aliases index)))
|
||||||
array))
|
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 []
|
(defn- create-function-repository []
|
||||||
(let [repository (new DefaultFunctionRepository (make-array JtwigFunction 0))]
|
(doto (new DefaultFunctionRepository (make-array JtwigFunction 0))
|
||||||
; always add our standard functions to new repository objects
|
(add-function-library! standard-functions)
|
||||||
(doseq [[name {:keys [aliases fn]}] standard-functions]
|
(add-function-library! web-functions)))
|
||||||
(.add repository
|
|
||||||
(make-function-handler fn)
|
|
||||||
name
|
|
||||||
(make-aliased-array aliases)))
|
|
||||||
repository))
|
|
||||||
|
|
||||||
; we'll be reusing the same function repository object for all contexts created when rendering templates.
|
; 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
|
; any custom functions added will be added to this instance
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
(ns clj-jtwig.standard-functions
|
(ns clj-jtwig.standard-functions
|
||||||
"standard function definitions. these are functions that are not yet included in JTwig's standard function
|
"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."
|
library and are just here to fill in the gaps for now."
|
||||||
|
(:import (org.apache.commons.lang3.text WordUtils)
|
||||||
|
(org.apache.commons.lang3 StringUtils))
|
||||||
(:use [clojure.pprint]))
|
(:use [clojure.pprint]))
|
||||||
|
|
||||||
; we are using a separate map to hold the standard functions instead of using deftwigfn, etc. because doing it this
|
; we are using a separate map to hold the standard functions instead of using deftwigfn, etc. because doing it this
|
||||||
|
@ -23,6 +25,14 @@
|
||||||
(-> sequence vals butlast)
|
(-> sequence vals butlast)
|
||||||
(butlast sequence)))}
|
(butlast sequence)))}
|
||||||
|
|
||||||
|
"capitalize_all"
|
||||||
|
{:fn (fn [s]
|
||||||
|
(WordUtils/capitalize s))}
|
||||||
|
|
||||||
|
"center"
|
||||||
|
{:fn (fn [s size & [padding-string]]
|
||||||
|
(StringUtils/center s size (or padding-string " ")))}
|
||||||
|
|
||||||
"dump"
|
"dump"
|
||||||
{:fn (fn [x]
|
{:fn (fn [x]
|
||||||
(with-out-str
|
(with-out-str
|
||||||
|
@ -33,15 +43,6 @@
|
||||||
(with-out-str
|
(with-out-str
|
||||||
(clojure.pprint/print-table x)))}
|
(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"
|
"max"
|
||||||
{:fn (fn [& numbers]
|
{:fn (fn [& numbers]
|
||||||
(if (coll? (first numbers))
|
(if (coll? (first numbers))
|
||||||
|
@ -54,6 +55,27 @@
|
||||||
(apply min (first numbers))
|
(apply min (first numbers))
|
||||||
(apply min numbers)))}
|
(apply min numbers)))}
|
||||||
|
|
||||||
|
"normalize_space"
|
||||||
|
{: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 " ")))}
|
||||||
|
|
||||||
|
"pad_right"
|
||||||
|
{:fn (fn [s size & [padding-string]]
|
||||||
|
(StringUtils/rightPad s size (or padding-string " ")))}
|
||||||
|
|
||||||
"random"
|
"random"
|
||||||
{:fn (fn [& values]
|
{:fn (fn [& values]
|
||||||
(let [first-value (first values)]
|
(let [first-value (first values)]
|
||||||
|
@ -78,6 +100,10 @@
|
||||||
{:fn (fn [low high & [step]]
|
{:fn (fn [low high & [step]]
|
||||||
(range low high (or step 1)))}
|
(range low high (or step 1)))}
|
||||||
|
|
||||||
|
"repeat"
|
||||||
|
{:fn (fn [s n]
|
||||||
|
(StringUtils/repeat s n))}
|
||||||
|
|
||||||
"rest"
|
"rest"
|
||||||
{:fn (fn [sequence]
|
{:fn (fn [sequence]
|
||||||
; matching behaviour of jtwig's first/last implementation
|
; matching behaviour of jtwig's first/last implementation
|
||||||
|
@ -108,4 +134,14 @@
|
||||||
"sort_descending_by"
|
"sort_descending_by"
|
||||||
{:fn (fn [coll k]
|
{:fn (fn [coll k]
|
||||||
(sort-by #(get % k) #(compare %2 %1) coll))
|
(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?)))}})
|
||||||
|
|
51
src/clj_jtwig/utils.clj
Normal file
51
src/clj_jtwig/utils.clj
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
(ns clj-jtwig.utils
|
||||||
|
"various helper / utility functions"
|
||||||
|
(:import (java.net URL)
|
||||||
|
(java.io File)
|
||||||
|
(java.util.jar JarFile)))
|
||||||
|
|
||||||
|
(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-jar-resource-filename [^String resource-filename]
|
||||||
|
(let [pos (.indexOf resource-filename "jar!")]
|
||||||
|
(if-not (= -1 pos)
|
||||||
|
(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)
|
||||||
|
(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]
|
||||||
|
(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))))
|
12
src/clj_jtwig/web/middleware.clj
Normal file
12
src/clj_jtwig/web/middleware.clj
Normal file
|
@ -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))))
|
43
src/clj_jtwig/web/web_functions.clj
Normal file
43
src/clj_jtwig/web/web_functions.clj
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
(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."
|
||||||
|
(: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]
|
||||||
|
(get-context-url url))}
|
||||||
|
|
||||||
|
"stylesheet"
|
||||||
|
{:fn (fn [url & [media]]
|
||||||
|
(let [fmt (if media
|
||||||
|
"<link href=\"%s\" rel=\"stylesheet\" type=\"text/css\" media=\"%s\" />"
|
||||||
|
"<link href=\"%s\" rel=\"stylesheet\" type=\"text/css\" />")]
|
||||||
|
(format fmt (get-url-string url) media)))}
|
||||||
|
|
||||||
|
"javascript"
|
||||||
|
{:fn (fn [url]
|
||||||
|
(format "<script type=\"text/javascript\" src=\"%s\"></script>" (get-url-string url)))}})
|
|
@ -284,6 +284,18 @@
|
||||||
(is (= (render "{{ [1, 2, 3, 4, 5]|butlast }}" nil)
|
(is (= (render "{{ [1, 2, 3, 4, 5]|butlast }}" nil)
|
||||||
"[1, 2, 3, 4]")))
|
"[1, 2, 3, 4]")))
|
||||||
|
|
||||||
|
(testing "capitalize_all"
|
||||||
|
(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"
|
(testing "dump"
|
||||||
(is (= (render "{{ a|dump }}" {:a [{:foo "bar"} [1, 2, 3] "hello"]})
|
(is (= (render "{{ a|dump }}" {:a [{:foo "bar"} [1, 2, 3] "hello"]})
|
||||||
"({\"foo\" \"bar\"} (1 2 3) \"hello\")\n")))
|
"({\"foo\" \"bar\"} (1 2 3) \"hello\")\n")))
|
||||||
|
@ -292,16 +304,6 @@
|
||||||
(is (= (render "{{ t|dump_table }}", {:t [{:a 1 :b 2 :c 3} {:b 5 :a 7 :c "dog"}]})
|
(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")))
|
"\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"
|
(testing "max"
|
||||||
(is (= (render "{{ [2, 1, 5, 3, 4]|max }}" nil)
|
(is (= (render "{{ [2, 1, 5, 3, 4]|max }}" nil)
|
||||||
"5"))
|
"5"))
|
||||||
|
@ -314,6 +316,36 @@
|
||||||
(is (= (render "{{ min(2, 1, 5, 3, 4) }}" nil)
|
(is (= (render "{{ min(2, 1, 5, 3, 4) }}" nil)
|
||||||
"1")))
|
"1")))
|
||||||
|
|
||||||
|
(testing "normalize_space"
|
||||||
|
(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"))
|
||||||
|
(is (= (render "{{ pad_left('bat', 3) }}" nil)
|
||||||
|
"bat"))
|
||||||
|
(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"
|
(testing "random"
|
||||||
(is (some #{(render "{{ ['apple', 'orange', 'citrus']|random }}" nil)}
|
(is (some #{(render "{{ ['apple', 'orange', 'citrus']|random }}" nil)}
|
||||||
["apple" "orange" "citrus"]))
|
["apple" "orange" "citrus"]))
|
||||||
|
@ -326,6 +358,12 @@
|
||||||
(is (= (render "{{ range(1, 5, 2) }}" nil)
|
(is (= (render "{{ range(1, 5, 2) }}" nil)
|
||||||
"[1, 3]")))
|
"[1, 3]")))
|
||||||
|
|
||||||
|
(testing "repeat"
|
||||||
|
(is (= (render "{{ repeat('x', 10) }}" nil)
|
||||||
|
"xxxxxxxxxx"))
|
||||||
|
(is (= (render "{{ repeat('x', 0) }}" nil)
|
||||||
|
"")))
|
||||||
|
|
||||||
(testing "rest"
|
(testing "rest"
|
||||||
(is (= (render "{{ [1, 2, 3, 4, 5]|rest }}" nil)
|
(is (= (render "{{ [1, 2, 3, 4, 5]|rest }}" nil)
|
||||||
"[2, 3, 4, 5]")))
|
"[2, 3, 4, 5]")))
|
||||||
|
@ -348,4 +386,14 @@
|
||||||
|
|
||||||
(testing "sort_descending_by"
|
(testing "sort_descending_by"
|
||||||
(is (= (render "{{ [{a: 2}, {a: 1}, {a: 5}, {a: 3}, {a: 4}]|sort_descending_by(\"a\") }}" nil)
|
(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, \"<br />\") }}" nil)
|
||||||
|
"Here is one line of<br />text that is going<br />to be wrapped after<br />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"))))
|
||||||
|
|
Reference in a new issue