[bknr-cvs] hans changed trunk/

BKNR Commits bknr at bknr.net
Thu Jul 31 06:18:03 UTC 2008


Revision: 3702
Author: hans
URL: http://bknr.net/trac/changeset/3702

Add streaming JSON encoding infrastructure and handler for news.

U   trunk/bknr/web/src/rss/rss.lisp
U   trunk/projects/quickhoney/src/handlers.lisp

Modified: trunk/bknr/web/src/rss/rss.lisp
===================================================================
--- trunk/bknr/web/src/rss/rss.lisp	2008-07-30 21:06:43 UTC (rev 3701)
+++ trunk/bknr/web/src/rss/rss.lisp	2008-07-31 06:18:02 UTC (rev 3702)
@@ -117,6 +117,12 @@
       (when days-string
         (parse-integer days-string)))))
 
+(defun month-from-query-parameter ()
+  (when (boundp 'hunchentoot:*request*)
+    (let ((month-string (bknr.web:query-param "month")))
+      (when month-string
+        (mapcar #'parse-integer (cl-ppcre:split "([-/]|(?<=..))" month-string :limit 2))))))
+
 (defun rss-channel-archive (channel)
   "Return the channel archive consisting of lists of lists ((MONTH YEAR) ITEM...)"
   (group-on (rss-channel-items channel)
@@ -129,18 +135,21 @@
 
 (defgeneric rss-channel-items (channel &key)
   (:documentation "Return all non-expired items in channel.")
-  (:method ((channel rss-channel) &key days month)
-    (cond
-      (month
-       (cdr (find month (rss-channel-archive channel) :test #'equal)))
-      (t
-       (let* ((days (or days
-                        (days-from-query-parameter)
-                        (rss-channel-max-item-age channel)))
-              (expiry-time (- (get-universal-time) (* 60 60 25 days))))
-         (remove-if (lambda (item) (or (object-destroyed-p item)
-                                       (< (rss-item-pub-date item) expiry-time)))
-                    (slot-value channel 'items)))))))
+  (:method ((channel rss-channel) &key days month count)
+    (unless month
+      (setf month (month-from-query-parameter)))
+    (unless days
+      (setf days (or (days-from-query-parameter)
+                     (rss-channel-max-item-age channel))))
+    (let ((items (if month
+                     (cdr (find month (rss-channel-archive channel) :test #'equal))
+                     (let ((expiry-time (- (get-universal-time) (* 60 60 24 days))))
+                       (remove-if (lambda (item) (or (object-destroyed-p item)
+                                                     (< (rss-item-pub-date item) expiry-time)))
+                                  (slot-value channel 'items))))))
+      (if count
+          (subseq items 0 (min count (length items)))
+          items))))
 
 (defgeneric rss-channel-archived-months (channel)
   (:documentation "Return a list of lists (MONTH YEAR) for which the

Modified: trunk/projects/quickhoney/src/handlers.lisp
===================================================================
--- trunk/projects/quickhoney/src/handlers.lisp	2008-07-30 21:06:43 UTC (rev 3701)
+++ trunk/projects/quickhoney/src/handlers.lisp	2008-07-31 06:18:02 UTC (rev 3702)
@@ -432,49 +432,61 @@
 
 (defvar *json-output*)
 
+(defclass json-output-stream ()
+  ((stream :reader stream
+           :initarg :stream)
+   (stack :accessor stack
+          :initform nil)))
+
+(defun next-aggregate-element ()
+  (if (car (stack *json-output*))
+      (princ #\, (stream *json-output*))
+      (setf (car (stack *json-output*)) t)))
+
 (defmacro with-json-output ((stream) &body body)
-  `(let ((*json-output* ,stream))
+  `(let ((*json-output* (make-instance 'json-output-stream :stream ,stream)))
      , at body))
 
 (defmacro with-json-output-to-string (() &body body)
-  `(with-output-to-string (*json-output*)
-     , at body))
+  `(with-output-to-string (s)
+     (with-json-output (s)
+       , at body)))
 
+(defmacro with-json-aggregate ((begin-char end-char) &body body)
+  `(progn
+     (when (stack *json-output*)
+       (next-aggregate-element))
+     (princ ,begin-char (stream *json-output*))
+     (push nil (stack *json-output*))
+     (prog1
+         (progn , at body)
+       (pop (stack *json-output*))
+       (princ ,end-char (stream *json-output*)))))
+
 (defmacro with-json-array (() &body body)
-  (with-gensyms (need-comma)
-    `(let (,need-comma)
-       (princ #\[ *json-output*)
-       (prog1
-           (labels ((encode-array-element (value)
-                      (if ,need-comma
-                          (princ #\, *json-output*)
-                          (setf ,need-comma t))
-                      (json:encode-json value *json-output*)))
-             , at body)
-         (princ #\] *json-output*)))))
+  `(with-json-aggregate (#\[ #\])
+     , at body))
 
 (defmacro with-json-object (() &body body)
-  (with-gensyms (need-comma)
-    `(let (,need-comma)
-       (princ #\{ *json-output*)
-       (prog1
-           (labels ((encode-object-member (key value)
-                      (when value
-                        (if ,need-comma
-                            (princ #\, *json-output*)
-                            (setf ,need-comma t))
-                        (json:encode-json key *json-output*)
-                        (princ #\, *json-output*)
-                        (json:encode-json value *json-output*))))
-             , at body)
-         (princ #\} *json-output*)))))
+  `(with-json-aggregate (#\{ #\})
+     , at body))
 
+(defun encode-array-element (object)
+  (next-aggregate-element)
+  (json:encode-json object (stream *json-output*)))
+
+(defun encode-object-element (key value)
+  (next-aggregate-element)
+  (json:encode-json key (stream *json-output*))
+  (princ #\: (stream *json-output*))
+  (json:encode-json value (stream *json-output*)))
+
 (defmethod handle-object ((handler news-json-handler) (channel rss-channel))
   (with-http-response (:content-type "application/json")
     (with-json-output-to-string ()
       (with-json-array ()
         (dolist (item (rss-channel-items channel))
           (with-json-object ()
-            (encode-object-member "pubDate" (format-date-time (rss-item-pub-date item) :vms-style t))
-            (encode-object-member "title" (rss-item-title item))
-            (encode-object-member "description" (rss-item-description item))))))))
\ No newline at end of file
+            (encode-object-element "pubDate" (format-date-time (rss-item-pub-date item) :vms-style t))
+            (encode-object-element "title" (rss-item-title item))
+            (encode-object-element "description" (rss-item-description item))))))))




More information about the Bknr-cvs mailing list