Advertisement
Guest User

Untitled

a guest
Jul 27th, 2017
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.85 KB | None | 0 0
  1. (defpackage :who-templates
  2. (:nicknames :whot)
  3. (:use :cl)
  4. (:export
  5. :deftemplate
  6. :block
  7. :include
  8. :parent
  9. :render-template
  10. :render-template-to-string
  11. :targ
  12. :with-targs))
  13.  
  14. (in-package :whot)
  15.  
  16. (defvar *templates* (make-hash-table))
  17. (defvar *template*)
  18. (defvar *block*)
  19. (defvar *template-args*)
  20. (defvar *out*)
  21.  
  22. (defclass who-template ()
  23. ((name :initarg :name
  24. :accessor template-name
  25. :type symbol
  26. :initform (error "Provide the template name"))
  27. (parent :initarg :parent
  28. :accessor template-parent
  29. :type (or null symbol)
  30. :initform nil)
  31. (renderer :initarg :renderer
  32. :accessor template-renderer
  33. :type (or null function)
  34. :initform nil)
  35. (blocks :initarg :blocks
  36. :accessor template-blocks
  37. :initform nil)))
  38.  
  39. (defmethod print-object ((template who-template) stream)
  40. (print-unreadable-object (template stream :type t :identity t)
  41. (format stream "~A ~[parent:~A~]"
  42. (template-name template)
  43. (template-parent template))))
  44.  
  45. (defun register-template (template)
  46. (setf (gethash (template-name template) *templates*) template))
  47.  
  48. (defun find-template (name)
  49. (or (gethash name *templates*)
  50. (error "Template not defined: ~A" name)))
  51.  
  52. (defmethod initialize-instance :after ((template who-template) &rest initargs)
  53. (declare (ignore initargs))
  54. (when (and (template-renderer template)
  55. (template-parent template))
  56. (error "Template ~A: Cannot have body and parent at the same time"
  57. (template-name template)))
  58. ;; Register the tamplate
  59. (register-template template))
  60.  
  61. (defun targ (symbol)
  62. (getf *template-args* symbol))
  63.  
  64. (defun args-list-p (form)
  65. (and (listp form)
  66. (equalp (string (first form)) "&args")))
  67.  
  68. (defun expand-body (body)
  69. (if (args-list-p (first body))
  70. (let ((args (cdr (first body))))
  71. (list `(let ,(loop for arg in args
  72. collect `(,arg (targ ,(intern (string arg) :keyword))))
  73. (who:htm ,@(rest body)))))
  74. body))
  75.  
  76. (defmacro with-targs (args &body body)
  77. `(let ,(loop for arg in args
  78. collect `(,arg (targ ,(intern (string arg) :keyword))))
  79. (who:htm ,@body)))
  80.  
  81. (defun collect-replace-blocks (form)
  82. (let (blocks)
  83. (let ((new-form (%collect-replace-blocks
  84. form
  85. (lambda (block) (push block blocks)))))
  86. (values new-form blocks))))
  87.  
  88. (defun %collect-replace-blocks (form collect-block)
  89. (if (atom form)
  90. form
  91. (if (eql (first form) 'block)
  92. (progn
  93. (funcall collect-block (cdr form))
  94. `(render-block ',(second form)))
  95. (loop for part in form
  96. collect
  97. (%collect-replace-blocks part collect-block)))))
  98.  
  99. (defun find-block (name template)
  100. (let ((block (cdr (assoc name (template-blocks template)))))
  101. (if block
  102. (values block template)
  103. (and (template-parent template)
  104. (find-block name (find-template (template-parent template)))))))
  105.  
  106. (defun render-block (block-name)
  107. (let ((block (find-block block-name *template*)))
  108. (when block
  109. (funcall block))))
  110.  
  111. (defun parent (&optional (block *block*) (template (template-parent *template*)))
  112. "Render the parent block"
  113. (let ((parent-template (find-template template)))
  114. (multiple-value-bind (parent-block *template*)
  115. (find-block block parent-template)
  116. (when parent-block
  117. (funcall parent-block)))))
  118.  
  119. (defun include (template-name)
  120. (funcall (template-renderer (find-template template-name))))
  121.  
  122. (defun find-renderer (template)
  123. (if (template-parent template)
  124. (find-renderer (find-template (template-parent template)))
  125. (template-renderer template)))
  126.  
  127. (defun parse-template (body)
  128. (collect-replace-blocks body))
  129.  
  130. (defmacro deftemplate (name args &body body)
  131. (multiple-value-bind (new-body blocks)
  132. (parse-template body)
  133. `(make-instance 'who-template
  134. :name ',name
  135. :parent ',(getf args :parent)
  136. :renderer ,(when (not (getf args :parent))
  137. `(lambda ()
  138. (who:with-html-output (html *out*)
  139. ,@(expand-body new-body))))
  140. :blocks (list ,@(loop for block in blocks
  141. collect `(cons ',(car block)
  142. (lambda ()
  143. (let ((*block* ',(car block))
  144. (*template* (find-template ',name)))
  145. (who:with-html-output (html *out*)
  146. ,@(expand-body (cdr block)))))))))))
  147.  
  148. (defun render-template-to-string (name &rest args)
  149. (with-output-to-string (*out*)
  150. (apply #'render-template name *out* args)))
  151.  
  152. (defun render-template (name stream &rest args)
  153. (let ((*template* (find-template name))
  154. (*template-args* args)
  155. (*out* stream))
  156. (let ((renderer (find-renderer *template*)))
  157. (when (not renderer)
  158. (error "Don't know how to render template ~A" name))
  159. (funcall renderer))))
  160.  
  161. ;;--------------------
  162. ;; Example
  163. ;;--------------------
  164.  
  165. ;; Base template example
  166. (deftemplate base-1 ()
  167. (&args title)
  168. (:html
  169. (:head
  170. (:title (who:str (or title "WHO TEMPLATES")))
  171. (block styles
  172. (:link :rel "stylesheet" :href "/bootstrap.css")))
  173. (:body
  174. (block body)
  175. (block scripts))))
  176.  
  177. (render-template-to-string 'base-1)
  178. (render-template-to-string 'base-1 :title "lala")
  179.  
  180. ;; Inheritance/block overwrite. Calls to parent
  181.  
  182. (deftemplate foo (:parent base-1)
  183. (block body
  184. (:h1 (who:str "Foo"))))
  185.  
  186. (render-template-to-string 'foo)
  187.  
  188. (deftemplate bar (:parent base-1)
  189. (block body
  190. (:h1 (who:str "Bar")))
  191. (block styles
  192. (parent)
  193. (:link :rel "stylesheet" :href "/bar.css")))
  194.  
  195. (render-template-to-string 'bar)
  196.  
  197.  
  198. (deftemplate baz (:parent bar)
  199. (block scripts
  200. (parent)
  201. (:script :type "text/javacript"
  202. (who:str "...javascript code..."))))
  203.  
  204. (render-template-to-string 'baz)
  205.  
  206. ;; Args
  207.  
  208. (deftemplate hello (:parent base-1)
  209. (block body
  210. (:h1 (who:str (targ :hello)))))
  211.  
  212. (render-template-to-string 'hello :hello "Hello!!")
  213.  
  214. (deftemplate hello-2 (:parent base-1)
  215. (block body
  216. (&args hello)
  217. (:h1 (who:str hello))
  218. (:h2 (who:str hello))))
  219.  
  220. (render-template-to-string 'hello-2 :hello "Hi!!")
  221.  
  222. (deftemplate hello-3 (:parent base-1)
  223. (block body
  224. (with-targs (hello)
  225. (:h1 (who:str hello))
  226. (:h2 (who:str hello)))))
  227.  
  228. (render-template-to-string 'hello-3 :hello "Hi!!")
  229.  
  230. ;; Include
  231. (deftemplate snippet ()
  232. (:p (who:str "This stuff has been included")))
  233.  
  234. (deftemplate include (:parent base-1)
  235. (block body
  236. (include 'snippet)))
  237.  
  238. (render-template-to-string 'include)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement