Libraries

Sytes provides a few ways for you to create library functions (that is, to write functions in one place and use them in various templates).

.boot.syt

The easiest to start with is the .boot.syt file in the toplevel (root) directory of your Syte. That file is evaluated in the Syte context so whatever you define there will be available in all templates. Simple. For example this syte's .boot.syt looks like this:

{(progn ; -*- lisp -*-

   (defun push-breadcrumb (name url)
     (@attr 'PATH
            (cons (list name (absurl url))
                  (@attr 'PATH))))

   (defun push-subcontent (html)
     (@attr 'SUBCONTENT
            (cons html (@attr 'SUBCONTENT))))

   (defun push-operation (html)
     (@attr 'OPERATIONS
            (cons html (@attr 'OPERATIONS))))

   (defun push-script (url)
     (@attr 'SCRIPTS
            (cons url (@attr 'SCRIPTS))))

   (defun push-style (url)
     (@attr 'STYLES
            (cons url (@attr 'STYLES))))

   (defun fork-me-on-github (url)
     (unless (@attr 'FORK-ME-ON-GITHUB)
       (@attr 'FORK-ME-ON-GITHUB url)))

   (defun page-link (url text)
     (define absolute (absurl url))
     (define current (sameurl absolute))
     (define class nil)
     (when (car current) (push "current" class))
     (when (cdr current) (push "inside" class))
     (when class
       (set! class (fmt "class='~{~A~^ ~}'" class)))
     {<a href="{\absolute}" {class}>{\text}</a>})

   (defun hl-lisp (code)
     {<code class="ColoroloC" lang="lisp">{\code}</code>})

   (defun HL-lisp (code)
     {<pre class="ColoroloC" lang="lisp">{\code}</pre>})

   (defun hl-syt (code)
     {<code class="ColoroloC" lang="syt">{\code}</code>})

   (defun HL-syt (code)
     {<pre class="ColoroloC" lang="syt">{\code}</pre>})

   (defun HL-sample (left right){
     <table class="syt-example">
       <tr>
         <td class="sample">{left}</td>
         <td class="arrow">→</td>
         <td class="output">{right}</td>
       </tr>
     </table>})

   (defun HL-syt-example (code){
     <table class="syt-example">
       <tr>
         <td class="sample">
           <pre class="ColoroloC" lang="syt">{\code}</pre>
         </td>
         <td class="arrow">→</td>
         <td class="output">
           <pre class="ColoroloC" lang="xml">{
             \((compile `(lambda () ,(parse-syte code)) t))
           }</pre>
         </td>
       </tr>
     </table>})

   (defun HL-lisp2lisp (code1 code2){
     <table class="syt-example">
       <tr>
         <td class="sample">
           <pre class="ColoroloC" lang="lisp">{\code1}</pre>
         </td>
         <td class="arrow">→</td>
         <td class="output">
           <pre class="ColoroloC" lang="lisp">{\code2}</pre>
         </td>
       </tr>
     </table>})

   (defun HL-lisp2jljs (code1 code2){
     <table class="syt-example">
       <tr>
         <td class="sample">
           <pre class="ColoroloC" lang="lisp">{\code1}</pre>
         </td>
         <td class="arrow">→</td>
         <td class="output">
           <pre class="ColoroloC" lang="js">{\code2}</pre>
         </td>
       </tr>
     </table>})

   (defun HL-transform-sample (left right){
     <table class="syt-example">
       <tr>
         <td class="sample">{left}</td>
         <td class="arrow">→</td>
         <td class="output">{right}</td>
       </tr>
     </table>})

   (defun hl-xml (code)
     {<code class="ColoroloC" lang="xml">{\code}</code>})

   (defun HL-xml (code)
     {<pre class="ColoroloC" lang="xml">{\code}</pre>})

   (defun hl-js (code)
     {<code class="ColoroloC" lang="js">{\code}</code>})

   (defun HL-js (code attr)
     {<pre class="ColoroloC" lang="js" {attr}>{\code}</pre>})

   (defun HL-js* (code attr)
     {<pre class="ColoroloC" lang="js" {attr}>{code}</pre>})

   (defun HL-sh (code attr)
     {<pre class="ColoroloC" lang="sh" {attr}>{code}</pre>})

   (defun special (x) {<span class="special">{\x}</span>})

   (defun set-title (title) (@attr 'TITLE title))

   (define-filter upcase (string) (string-upcase string))

   (define-filter reverse (str) (reverse str))

   (define-filter date (timestamp format . timezone)
     (if (nonblank timestamp)
         (date/format timestamp format (car timezone))
         "—"))

   (define-filter json (exp)
     (json/encode exp))

   (defun l-sym (name)
     {<code class="ColoroloC" lang="lisp">{\name}</code>})

   (defun l-code (code)
     {<pre class="ColoroloC" lang="lisp">{\code}</pre>})

   (defmacro if-logged-in (then else)
     `(let ((user (current-user)))
        (if user ,then ,else)))

   (defmacro awhen (cond . body)
     `(let ((it ,cond))
        (when it
          ,@body)))

   (define-filter plural (texts n)
     (regexp-replace #/#/
                     (if (>= n (length texts))
                         (elt texts (1- (length texts)))
                         (elt texts n))
                     (strcat n)))

   (defun TODO (msg)
     {<span class="TODO">{\msg}</span>})

   (defun anchor (name content)
     {<a class="page-anchor" name="{\name}">{content}</a>})

   ;; HTML support

   (defun ~P (text) {<p>{text}</p>})
   (defun ~UL items
     {<ul>{(foreach item items {<li>{item}</li>})}</ul>})
   (defun ~OL items
     {<ol>{(foreach item items {<li>{item}</li>})}</ol>})

   nil

)}

They're just utility functions that I use in pretty much all pages, so it made sense to define them in a file that makes them globally available. Note, again, that if you run multiple Sytes in the same Lisp image, they'll still have separate contexts therefore stuff you define in one Syte's .boot.syt will not interfere with whatever you define in another.

require

Sytes provides a way to import definitions from another template via the require primitive. It takes a single argument—the name of the template to import from. That's a file name, and it's treated relative to the current template, unless it's an absolute file name (i.e. starts with a slash), in which case it'll be treated as relative to the Syte root directory. I wrote about it elsewhere.

Fork me on Github