Browse code
Dynamic view changing + other goodness
Add a switch-view restart that is wrapped around the controller and
changes the view to be executed at runtime. Useful to deal with cases
where an exceptional circumstance makes the original route suboptimal
Showing 4 changed files
... | ... |
@@ -13,7 +13,7 @@ |
13 | 13 |
|
14 | 14 |
(defgeneric view (name model) |
15 | 15 |
(:documentation "specialized on NAME with an EQL-specializer. This generic function |
16 |
- renders the model picked out by the controller. Normally, this is |
|
16 |
+ renders the model picked out by the controller. Normally, this is |
|
17 | 17 |
specialized using the DEFINE-VIEW macr")) |
18 | 18 |
|
19 | 19 |
(defmacro setf1 (&body body) |
... | ... |
@@ -29,18 +29,48 @@ |
29 | 29 |
(loop for ((target &key method) callback) in routes |
30 | 30 |
collect `((ningle:route ,app ,target :method ,(or method :GET)) ,callback))))) |
31 | 31 |
|
32 |
- |
|
32 |
+ |
|
33 |
+(defvar *view-name*) |
|
33 | 34 |
(defmacro as-route (name &rest r &key &allow-other-keys) |
34 | 35 |
"Create a lambda directing requests to the route for NAME. This uses the |
35 | 36 |
generic function RUN-ROUTE internally whose default implementation relies on |
36 | 37 |
appropriate implementations of CONTROLLER and VIEW. The RUN-ROUTE method receives |
37 | 38 |
the parameters ningle passes to other functions as a first parameter, and then it |
38 | 39 |
receives a bunch of arguments according to the arguments passed to this macro." |
39 |
- `(lambda (params) (run-route ,name params ,@r))) |
|
40 |
- |
|
40 |
+ (alexandria:once-only (name) |
|
41 |
+ `(lambda (params) |
|
42 |
+ (run-route ,name params ,@r)))) |
|
43 |
+ |
|
44 |
+(defun %compose-route (controller controller-args view) |
|
45 |
+ (declare (optimize (debug 3) (speed 0) (space 0) (safety 3))) |
|
46 |
+ (lambda (params) |
|
47 |
+ (declare (optimize (debug 3) (speed 0) (space 0) (safety 3))) |
|
48 |
+ (apply #'view |
|
49 |
+ (list view |
|
50 |
+ (apply #'controller |
|
51 |
+ (list* controller |
|
52 |
+ params |
|
53 |
+ controller-args)))))) |
|
54 |
+ |
|
55 |
+(defmacro compose-route ((controller &rest controller-args) view) |
|
56 |
+ `(%compose-route ',controller ,controller-args ',view)) |
|
57 |
+ |
|
58 |
+(defun switch-view (view-name) |
|
59 |
+ (format t "~&Switching view to: ~a~&" view-name) |
|
60 |
+ (alexandria:if-let ((switch-view-restart (find-restart 'switch-view))) |
|
61 |
+ (invoke-restart switch-view-restart view-name) |
|
62 |
+ (cerror "ignore this error" |
|
63 |
+ "Can only call switch-view while the route is being processed"))) |
|
41 | 64 |
|
42 | 65 |
(defmethod run-route (name params &rest r) |
43 |
- (view name (apply #'controller (list* name params r)))) |
|
66 |
+ (let ((*view-name* name)) |
|
67 |
+ (fw.lu:let-each (:be *) |
|
68 |
+ (list* name params r) |
|
69 |
+ (restart-bind ((switch-view (lambda (new-view) |
|
70 |
+ (format t "~%SWITCHING VIEW: ~a" new-view) |
|
71 |
+ (setf *view-name* new-view)))) |
|
72 |
+ (apply #'controller *)) |
|
73 |
+ (view *view-name* *)))) |
|
44 | 74 |
|
45 | 75 |
; The default controller just passes its parameters directly to the view |
46 | 76 |
(defmethod controller (name params &key) |