standard function now defined in a map which can be easily reloaded
This commit is contained in:
parent
f71272a8a5
commit
a7e6b70ea4
|
@ -5,17 +5,39 @@
|
|||
(com.lyncode.jtwig.functions.repository DefaultFunctionRepository)
|
||||
(com.lyncode.jtwig.functions.exceptions FunctionNotFoundException FunctionException))
|
||||
(:require [clj-jtwig.convert :refer [java->clojure clojure->java]])
|
||||
(:use [clojure.pprint]))
|
||||
(:use [clj-jtwig.standard-functions]))
|
||||
|
||||
(defn- make-function-handler [f]
|
||||
(reify JtwigFunction
|
||||
(execute [_ arguments]
|
||||
(try
|
||||
(clojure->java (apply f (map java->clojure arguments)))
|
||||
(catch Exception ex
|
||||
(throw (new FunctionException ex)))))))
|
||||
|
||||
(defn- make-aliased-array [aliases]
|
||||
(let [n (count aliases)
|
||||
array (make-array String n)]
|
||||
(doseq [index (range n)]
|
||||
(aset array index (nth aliases index)))
|
||||
array))
|
||||
|
||||
(defn- create-function-repository []
|
||||
(new DefaultFunctionRepository (make-array JtwigFunction 0)))
|
||||
(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))
|
||||
|
||||
; 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
|
||||
(defonce functions (atom (create-function-repository)))
|
||||
|
||||
(defn reset-functions!
|
||||
"removes any added custom template function handlers"
|
||||
"removes any added custom template function handlers. use this with care!"
|
||||
[]
|
||||
(reset! functions (create-function-repository)))
|
||||
|
||||
|
@ -26,13 +48,6 @@
|
|||
(catch FunctionNotFoundException ex
|
||||
false)))
|
||||
|
||||
(defn- make-aliased-array [aliases]
|
||||
(let [n (count aliases)
|
||||
array (make-array String n)]
|
||||
(doseq [index (range n)]
|
||||
(aset array index (nth aliases index)))
|
||||
array))
|
||||
|
||||
(defn add-function!
|
||||
"adds a new template function using the name specified. templates can call the function by the
|
||||
name specified (or one of the aliases specified) and passing in the same number of arguments
|
||||
|
@ -40,12 +55,7 @@
|
|||
then nil can be specified for the aliases arg.
|
||||
prefer to use the 'deftwigfn' macro when possible."
|
||||
[name aliases f]
|
||||
(let [handler (reify JtwigFunction
|
||||
(execute [_ arguments]
|
||||
(try
|
||||
(clojure->java (apply f (map java->clojure arguments)))
|
||||
(catch Exception ex
|
||||
(throw (new FunctionException ex))))))]
|
||||
(let [handler (make-function-handler f)]
|
||||
(.add @functions handler name (make-aliased-array aliases))
|
||||
(.retrieve @functions name)))
|
||||
|
||||
|
@ -64,83 +74,3 @@
|
|||
[fn-name args aliases & body]
|
||||
`(do
|
||||
(add-function! ~fn-name ~aliases (fn ~args ~@body))))
|
||||
|
||||
;; ============================================================================
|
||||
;; Standard functions
|
||||
;; ============================================================================
|
||||
|
||||
(deftwigfn "blankIfNull" [x]
|
||||
(if (nil? x) "" x))
|
||||
|
||||
(deftwigfn "butlast" [sequence]
|
||||
; matching behaviour of jtwig's first/last implementation
|
||||
(if (map? sequence)
|
||||
(-> sequence vals butlast)
|
||||
(butlast sequence)))
|
||||
|
||||
(deftwigfn "dump" [x]
|
||||
(with-out-str (clojure.pprint/pprint x)))
|
||||
|
||||
(deftwigfn "nth" [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))))
|
||||
|
||||
(deftwigfn "max" [& numbers]
|
||||
(if (coll? (first numbers))
|
||||
(apply max (first numbers))
|
||||
(apply max numbers)))
|
||||
|
||||
(deftwigfn "min" [& numbers]
|
||||
(if (coll? (first numbers))
|
||||
(apply min (first numbers))
|
||||
(apply min numbers)))
|
||||
|
||||
(deftwigfn "random" [& values]
|
||||
(let [first-value (first values)]
|
||||
(cond
|
||||
(and (= (count values) 1)
|
||||
(coll? first-value))
|
||||
(rand-nth first-value)
|
||||
|
||||
(> (count values) 1)
|
||||
(rand-nth values)
|
||||
|
||||
(string? first-value)
|
||||
(rand-nth (seq first-value))
|
||||
|
||||
(number? first-value)
|
||||
(rand-int first-value)
|
||||
|
||||
:else
|
||||
(rand))))
|
||||
|
||||
(deftwigfn "range" [low high & [step]]
|
||||
(range low high (or step 1)))
|
||||
|
||||
(deftwigfn "rest" [sequence]
|
||||
; matching behaviour of jtwig's first/last implementation
|
||||
(if (map? sequence)
|
||||
(-> sequence vals rest)
|
||||
(rest sequence)))
|
||||
|
||||
(deftwigfn "second" [sequence]
|
||||
; matching behaviour of jtwig's first/last implementation
|
||||
(if (map? sequence)
|
||||
(-> sequence vals second)
|
||||
(second sequence)))
|
||||
|
||||
(deftwigfn "sort" [sequence]
|
||||
(sort < sequence))
|
||||
|
||||
(deftwigfn "sortDescending" [sequence]
|
||||
(sort > sequence))
|
||||
|
||||
(deftwigfn "sortBy" [coll k]
|
||||
(sort-by #(get % k) coll))
|
||||
|
||||
(deftwigfn "sortDescendingBy" [coll k]
|
||||
(sort-by #(get % k) #(compare %2 %1) coll))
|
103
src/clj_jtwig/standard_functions.clj
Normal file
103
src/clj_jtwig/standard_functions.clj
Normal file
|
@ -0,0 +1,103 @@
|
|||
(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."
|
||||
(:use [clojure.pprint]))
|
||||
|
||||
; we are using a separate map to hold the standard functions instead of using deftwigfn, etc. because doing it this
|
||||
; way makes it easy to re-add all these functions when/if the JTwig function repository object needs to be
|
||||
; recreated (e.g. during unit tests).
|
||||
|
||||
; the keys are function names. each value is a map containing :fn which is the actual function, and an optional
|
||||
; :aliases, which should be a vector of strings containing one or more possible aliases for this function.
|
||||
|
||||
(defonce standard-functions
|
||||
{"blankIfNull"
|
||||
{:fn (fn [x]
|
||||
(if (nil? x) "" x))}
|
||||
|
||||
"butlast"
|
||||
{:fn (fn [sequence]
|
||||
; matching behaviour of jtwig's first/last implementation
|
||||
(if (map? sequence)
|
||||
(-> sequence vals butlast)
|
||||
(butlast sequence)))}
|
||||
|
||||
"dump"
|
||||
{:fn (fn [x]
|
||||
(with-out-str
|
||||
(clojure.pprint/pprint 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))
|
||||
(apply max (first numbers))
|
||||
(apply max numbers)))}
|
||||
|
||||
"min"
|
||||
{:fn (fn [& numbers]
|
||||
(if (coll? (first numbers))
|
||||
(apply min (first numbers))
|
||||
(apply min numbers)))}
|
||||
|
||||
"random"
|
||||
{:fn (fn [& values]
|
||||
(let [first-value (first values)]
|
||||
(cond
|
||||
(and (= (count values) 1)
|
||||
(coll? first-value))
|
||||
(rand-nth first-value)
|
||||
|
||||
(> (count values) 1)
|
||||
(rand-nth values)
|
||||
|
||||
(string? first-value)
|
||||
(rand-nth (seq first-value))
|
||||
|
||||
(number? first-value)
|
||||
(rand-int first-value)
|
||||
|
||||
:else
|
||||
(rand))))}
|
||||
|
||||
"range"
|
||||
{:fn (fn [low high & [step]]
|
||||
(range low high (or step 1)))}
|
||||
|
||||
"rest"
|
||||
{:fn (fn [sequence]
|
||||
; matching behaviour of jtwig's first/last implementation
|
||||
(if (map? sequence)
|
||||
(-> sequence vals rest)
|
||||
(rest sequence)))}
|
||||
|
||||
"second"
|
||||
{:fn (fn [sequence]
|
||||
; matching behaviour of jtwig's first/last implementation
|
||||
(if (map? sequence)
|
||||
(-> sequence vals second)
|
||||
(second sequence)))}
|
||||
|
||||
"sort"
|
||||
{:fn (fn [sequence]
|
||||
(sort < sequence))}
|
||||
|
||||
"sortDescending"
|
||||
{:fn (fn [sequence]
|
||||
(sort > sequence))}
|
||||
|
||||
"sortBy"
|
||||
{:fn (fn [coll k]
|
||||
(sort-by #(get % k) coll))}
|
||||
|
||||
"sortDescendingBy"
|
||||
{:fn (fn [coll k]
|
||||
(sort-by #(get % k) #(compare %2 %1) coll))}})
|
Reference in a new issue