git.fiddlerwoaroof.com
Browse code

Initial commit of web library

fiddlerwoaroof authored on 09/02/2016 05:15:43
Showing 5 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,2 @@
1
+.*.sw[a-z]
2
+*~
0 3
new file mode 100644
... ...
@@ -0,0 +1 @@
1
+This is the stub README.txt for the "araneus" project.
0 2
new file mode 100644
... ...
@@ -0,0 +1,15 @@
1
+;;;; araneus.asd
2
+
3
+(asdf:defsystem #:araneus
4
+  :description "Describe araneus here"
5
+  :author "Your Name <your.name@example.com>"
6
+  :license "Specify license here"
7
+  :depends-on (#:anaphora
8
+               #:alexandria
9
+               #:serapeum
10
+               #:cl-mustache)
11
+  :serial t
12
+  :components ((:file "package")
13
+               (:file "araneus")))
14
+
15
+;; vim: ft=lisp:
0 16
new file mode 100644
... ...
@@ -0,0 +1,61 @@
1
+;; This is a minimal web-framework built on ningle that attempts to decouple
2
+;; views from controllers and make life more interesting.
3
+(in-package :araneus)
4
+
5
+(defgeneric controller (name params &key))
6
+(defgeneric view (name model))
7
+(defgeneric run-route (name params &rest r))
8
+
9
+(defmacro setf1 (&body body)
10
+  "Make setf a bit nicer"
11
+  (list* 'setf (apply #'append body)))
12
+
13
+(defmacro defroutes (app &body routes)
14
+  (alexandria:once-only (app)
15
+    (list* 'setf1
16
+           (loop for ((target &key method) callback) in routes
17
+                 collect `((ningle:route ,app ,target :method ,(or method :GET)) ,callback)))))
18
+
19
+ 
20
+(defmacro as-route (name &rest r &key &allow-other-keys)
21
+  `(lambda (params) (run-route ,name params ,@r)))
22
+
23
+
24
+(defmethod run-route (name params &rest r)
25
+  (view name (apply #'controller (list* name params r))))
26
+
27
+(defmethod controller (name params &key)
28
+  params)
29
+
30
+(defmacro define-controller (name (params &rest r &key &allow-other-keys) &body body)
31
+  `(defmethod controller ((name (eql ',name)) ,params &key ,@r)
32
+     ,@body))
33
+
34
+(defmacro define-view (name (model) &body body)
35
+  `(defmethod view ((name (eql ',name)) ,model)
36
+     ,@body))
37
+
38
+(defun render-mustache (fn data)
39
+  (with-open-file (s (truename fn))
40
+    (let ((template (make-string (file-length s))))
41
+      (read-sequence template s)
42
+      (mustache:render* template data))))
43
+
44
+
45
+(defmacro mustache ((template lambda-list data) &body body)
46
+  "Template specifies the template to be render, lambda-list is used to destructure data
47
+   and body transforms the destructured data into an alist for use in the template"
48
+  (alexandria:once-only (template)
49
+    `(destructuring-bind ,lambda-list ,data
50
+       (render-mustache ,template
51
+                        (list
52
+                          ,@(loop for (k v) on body by #'cddr
53
+                                  if (or k v)
54
+                                  collect `(cons ,k ,v)))))))
55
+
56
+(defmacro mustache-view (name lambda-list template &body body)
57
+  (alexandria:with-gensyms (model)
58
+    `(define-view ,name (,model)
59
+       (mustache (,template ,lambda-list ,model)
60
+         ,@body))))
61
+
0 62
new file mode 100644
... ...
@@ -0,0 +1,9 @@
1
+;;;; package.lisp
2
+
3
+(defpackage #:araneus
4
+  (:use #:cl)
5
+  (:export #:defroutes #:as-route #:define-controller #:define-view
6
+           #:controller #:view #:run-route #:mustache-view #:render-mustache
7
+           #:setf1))
8
+
9
+