diff --git a/doc/clog-manual.html b/doc/clog-manual.html index 2da243c..c8c1f48 100644 --- a/doc/clog-manual.html +++ b/doc/clog-manual.html @@ -717,6 +717,25 @@ an application share per connection the same queue of serialized events.
Returns true if connection is valid on thisCLOG-OBJ.
+
+
+[macro] WITH-CONNECTION-CACHE (CLOG-OBJ) &BODY BODY
+ +Caches writes to the connection-id of CLOG-OBJ until
+flushed with FLUSH-CONNECTION-CACHE or a query is made.
[function] FLUSH-CONNECTION-CACHE CLOG-OBJ
+ +Flush connection cache if on.
CLOG-Obj - Internals for Extensions and Plugins
@@ -8151,10 +8170,11 @@ result or DEFAULT-ANSWER on time out.
[function] CLOG-REPL
+[function] CLOG-REPL &KEY (CLOG-GUI-INITIALIZE T) (CLOG-WEB-INITIALIZE T)
Set a path /repl that opens a blank page and sets the global -clog-user:body to last window openned to /repl.
diff --git a/source/clog-base.lisp b/source/clog-base.lisp
index 3666702..0983d15 100644
--- a/source/clog-base.lisp
+++ b/source/clog-base.lisp
@@ -14,7 +14,16 @@
;;; methods to retrieve connection-data (data that is associated with the
;;; current page regardless of object or thread of execution is lisp).
-(push :clog *features*)
+(pushnew :clog *features*)
+
+(defvar *connection-cache* nil
+ "Dynamic variable containing optional cache. Every thread has its
+own context and therefore its own copy of this variable when
+dynamically bound. As a result no thread protection is needed to
+access. To use dynamically bind the *connection-cache* and set it
+to (list :cache) turn on caching. By default this is off its main use
+is in initial setup complex pages. (private)
+See macro with-connection-cache.")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Implementation - clog-obj
@@ -84,9 +93,34 @@ during attachment. (Private)"))
dicarded, return CLOG-OBJ. (Internal)"))
(defmethod js-execute ((obj clog-obj) script)
- (clog-connection:execute (connection-id obj) script)
+ (if *connection-cache*
+ (push script *connection-cache*)
+ (clog-connection:execute (connection-id obj) script))
obj)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; with-connection-cache ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro with-connection-cache ((clog-obj) &body body)
+ "Caches writes to the connection-id of CLOG-OBJ until
+flushed with FLUSH-CONNECTION-CACHE or a query is made."
+ `(let ((*connection-cache* (list :cache)))
+ ,@body
+ (clog:flush-connection-cache ,clog-obj)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; flush-connection-cache ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun flush-connection-cache (clog-obj)
+ "Flush connection cache if on."
+ (when *connection-cache*
+ (dolist (script (reverse *connection-cache*))
+ (unless (eq script :cache)
+ (clog-connection:execute (connection-id clog-obj) script)))
+ (setf *connection-cache* (list :cache))))
+
;;;;;;;;;;;;;;
;; js-query ;;
;;;;;;;;;;;;;;
@@ -95,6 +129,7 @@ dicarded, return CLOG-OBJ. (Internal)"))
(:documentation "Execure SCRIPT on browser and return result. (Internal)"))
(defmethod js-query ((obj clog-obj) script &key (default-answer nil))
+ (flush-connection-cache obj)
(clog-connection:query (connection-id obj) script :default-answer default-answer))
;;;;;;;;;;;;;
diff --git a/source/clog.lisp b/source/clog.lisp
index d9fe3b3..7a52c49 100644
--- a/source/clog.lisp
+++ b/source/clog.lisp
@@ -113,6 +113,8 @@ embedded in a native template application.)"
(with-sync-event macro)
(remove-connection-data-item generic-function)
(validp generic-function)
+ (with-connection-cache macro)
+ (flush-connection-cache function)
"CLOG-Obj - Internals for Extensions and Plugins"
(html-id generic-function)
diff --git a/tutorial/09-tutorial.lisp b/tutorial/09-tutorial.lisp
index 684602b..7436189 100644
--- a/tutorial/09-tutorial.lisp
+++ b/tutorial/09-tutorial.lisp
@@ -6,154 +6,157 @@
(defun on-new-window (body)
(setf (title (html-document body)) "Tutorial 9")
- (let* (last-tab
- ;; Note: Since the there is no need to use the tmp objects
- ;; we reuse the same symbol name (tmp) even though the
- ;; compiler can mark those for garbage collection early
- ;; this not an issue as the element is created already
- ;; in the browser window. This is probably not the best
- ;; option for a production app though regardless.
- ;;
- ;; Create tabs and panels
- (t1 (create-button body :content "Tab1"))
- (t2 (create-button body :content "Tab2"))
- (t3 (create-button body :content "Tab3"))
- (tmp (create-br body))
- (p1 (create-div body))
- (p2 (create-div body))
- (p3 (create-div body :content "Panel3 - Type here"))
- ;; Create form for panel 1
- (f1 (create-form p1))
- (fe1 (create-form-element f1 :text
- :label (create-label f1 :content "Fill in blank:")))
- (tmp (create-br f1))
- (fe2 (create-form-element f1 :color :value "#ffffff"
- :label (create-label f1 :content "Pick a color:")))
- (tmp (create-br f1))
- (tmp (create-form-element f1 :submit :value "OK"))
- (tmp (create-form-element f1 :reset :value "Start Again"))
- ;; Create for for panel 2
- (f2 (create-form p2))
- (fs2 (create-fieldset f2 :legend "Stuff"))
- (tmp (create-label fs2 :content "Please type here:"))
- (ta1 (create-text-area fs2 :columns 60 :rows 8 :label tmp))
- (tmp (create-br fs2))
- (rd1 (create-form-element fs2 :radio :name "rd"))
- (tmp (create-label fs2 :content "To Be" :label-for rd1))
- (rd2 (create-form-element fs2 :radio :name "rd"))
- (tmp (create-label fs2 :content "No to Be" :label-for rd2))
- (tmp (create-br fs2))
- (ck1 (create-form-element fs2 :checkbox :name "ck"))
- (tmp (create-label fs2 :content "Here" :label-for ck1))
- (ck2 (create-form-element fs2 :checkbox :name "ck"))
- (tmp (create-label fs2 :content "There" :label-for ck2))
- (tmp (create-br fs2))
- (sl1 (create-select fs2 :label (create-label fs2 :content "Pick one:")))
- (sl2 (create-select fs2 :label (create-label fs2 :content "Pick one:")))
- (sl3 (create-select fs2 :multiple t
- :label (create-label fs2 :content "Pick some:")))
- (o1 (create-option sl3 :content "one"))
- (o2 (create-option sl3 :content "two"))
- (o3 (create-option sl3 :content "three"))
- (og1 (create-optgroup sl3 :content "These are a group"))
- (o4 (create-option og1 :content "four"))
- (o5 (create-option og1 :content "five"))
- (tmp (create-form-element f2 :submit :value "OK"))
- (tmp (create-form-element f2 :reset :value "Start Again")))
- (declare (ignore tmp))
+ ;; When doing extensive setup of a page using connection cache
+ ;; reduces rountrip traffic and speeds setup considerably.
+ (with-connection-cache (body)
+ (let* (last-tab
+ ;; Note: Since the there is no need to use the tmp objects
+ ;; we reuse the same symbol name (tmp) even though the
+ ;; compiler can mark those for garbage collection early
+ ;; this not an issue as the element is created already
+ ;; in the browser window. This is probably not the best
+ ;; option for a production app though regardless.
+ ;;
+ ;; Create tabs and panels
+ (t1 (create-button body :content "Tab1"))
+ (t2 (create-button body :content "Tab2"))
+ (t3 (create-button body :content "Tab3"))
+ (tmp (create-br body))
+ (p1 (create-div body))
+ (p2 (create-div body))
+ (p3 (create-div body :content "Panel3 - Type here"))
+ ;; Create form for panel 1
+ (f1 (create-form p1))
+ (fe1 (create-form-element f1 :text
+ :label (create-label f1 :content "Fill in blank:")))
+ (tmp (create-br f1))
+ (fe2 (create-form-element f1 :color :value "#ffffff"
+ :label (create-label f1 :content "Pick a color:")))
+ (tmp (create-br f1))
+ (tmp (create-form-element f1 :submit :value "OK"))
+ (tmp (create-form-element f1 :reset :value "Start Again"))
+ ;; Create for for panel 2
+ (f2 (create-form p2))
+ (fs2 (create-fieldset f2 :legend "Stuff"))
+ (tmp (create-label fs2 :content "Please type here:"))
+ (ta1 (create-text-area fs2 :columns 60 :rows 8 :label tmp))
+ (tmp (create-br fs2))
+ (rd1 (create-form-element fs2 :radio :name "rd"))
+ (tmp (create-label fs2 :content "To Be" :label-for rd1))
+ (rd2 (create-form-element fs2 :radio :name "rd"))
+ (tmp (create-label fs2 :content "No to Be" :label-for rd2))
+ (tmp (create-br fs2))
+ (ck1 (create-form-element fs2 :checkbox :name "ck"))
+ (tmp (create-label fs2 :content "Here" :label-for ck1))
+ (ck2 (create-form-element fs2 :checkbox :name "ck"))
+ (tmp (create-label fs2 :content "There" :label-for ck2))
+ (tmp (create-br fs2))
+ (sl1 (create-select fs2 :label (create-label fs2 :content "Pick one:")))
+ (sl2 (create-select fs2 :label (create-label fs2 :content "Pick one:")))
+ (sl3 (create-select fs2 :multiple t
+ :label (create-label fs2 :content "Pick some:")))
+ (o1 (create-option sl3 :content "one"))
+ (o2 (create-option sl3 :content "two"))
+ (o3 (create-option sl3 :content "three"))
+ (og1 (create-optgroup sl3 :content "These are a group"))
+ (o4 (create-option og1 :content "four"))
+ (o5 (create-option og1 :content "five"))
+ (tmp (create-form-element f2 :submit :value "OK"))
+ (tmp (create-form-element f2 :reset :value "Start Again")))
+ (declare (ignore tmp))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Panel 1 contents
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (setf (place-holder fe1) "type here..")
- (setf (requiredp fe1) t)
- (setf (size fe1) 60)
- (make-data-list fe1 '("Cool Title"
- "Not So Cool Title"
- "Why Not, Another Title"))
- (make-data-list fe2 '("#ffffff"
- "#ff0000"
- "#00ff00"
- "#0000ff"
- "#ff00ff"))
- (set-on-submit f1
- (lambda (obj)
- (declare (ignore obj))
- (setf (title (html-document body)) (value fe1))
- (setf (background-color p1) (value fe2))
- (setf (hiddenp f1) t)
- (create-span p1 :content
- "
Your form has been submitted")))
- (setf (width p1) "100%")
- (setf (width p2) "100%")
- (setf (width p3) "100%")
- (setf (height p1) 400)
- (setf (height p2) 400)
- (setf (height p3) 400)
- (set-border p1 :thin :solid :black)
- (set-border p2 :thin :solid :black)
- (set-border p3 :thin :solid :black)
+ ;; Panel 1 contents
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;; Panel 2 contents
+ (setf (place-holder fe1) "type here..")
+ (setf (requiredp fe1) t)
+ (setf (size fe1) 60)
+ (make-data-list fe1 '("Cool Title"
+ "Not So Cool Title"
+ "Why Not, Another Title"))
+ (make-data-list fe2 '("#ffffff"
+ "#ff0000"
+ "#00ff00"
+ "#0000ff"
+ "#ff00ff"))
+ (set-on-submit f1
+ (lambda (obj)
+ (declare (ignore obj))
+ (setf (title (html-document body)) (value fe1))
+ (setf (background-color p1) (value fe2))
+ (setf (hiddenp f1) t)
+ (create-span p1 :content
+ "
Your form has been submitted")))
+ (setf (width p1) "100%")
+ (setf (width p2) "100%")
+ (setf (width p3) "100%")
+ (setf (height p1) 400)
+ (setf (height p2) 400)
+ (setf (height p3) 400)
+ (set-border p1 :thin :solid :black)
+ (set-border p2 :thin :solid :black)
+ (set-border p3 :thin :solid :black)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (setf (vertical-align ta1) :top)
- (disable-resize ta1)
- (setf (vertical-align sl1) :top)
- (setf (vertical-align sl2) :top)
- (setf (vertical-align sl3) :top)
- (setf (size sl1) 3)
- (add-select-options sl1 '("one"
- "two"
- "three"
- "four"
- "five"))
- (add-select-options sl2 '("one"
- "two"
- "three"
- "four"
- "five"))
- (set-on-change sl3 (lambda (obj)
- (declare (ignore obj))
- (when (selectedp o5)
- (alert (window body) "Selected 5"))))
- (set-on-submit f2
- (lambda (obj)
- (declare (ignore obj))
- (setf (hiddenp f2) t)
- (create-span p2 :content
- (format nil "
Your form has been submitted:
+ ;; Panel 2 contents
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (setf (vertical-align ta1) :top)
+ (disable-resize ta1)
+ (setf (vertical-align sl1) :top)
+ (setf (vertical-align sl2) :top)
+ (setf (vertical-align sl3) :top)
+ (setf (size sl1) 3)
+ (add-select-options sl1 '("one"
+ "two"
+ "three"
+ "four"
+ "five"))
+ (add-select-options sl2 '("one"
+ "two"
+ "three"
+ "four"
+ "five"))
+ (set-on-change sl3 (lambda (obj)
+ (declare (ignore obj))
+ (when (selectedp o5)
+ (alert (window body) "Selected 5"))))
+ (set-on-submit f2
+ (lambda (obj)
+ (declare (ignore obj))
+ (setf (hiddenp f2) t)
+ (create-span p2 :content
+ (format nil "
Your form has been submitted:
~A