Browse code
Splitting RSS support out of main lisp file
- Added local-time based parsing and printing of dates.
- Changed specialization of %generate-xml so that it can generate an RSS
feed from an arbitrary object.
Showing 7 changed files
... | ... |
@@ -1,30 +1,107 @@ |
1 | 1 |
;;;; alimenta.lisp |
2 | 2 |
(declaim (optimize (speed 0) (safety 3) (debug 3))) |
3 | 3 |
|
4 |
- |
|
5 | 4 |
(in-package #:alimenta) |
6 | 5 |
|
7 |
-;;; "alimenta" goes here. Hacks and glory await! |
|
8 |
-(defun detect-feed-type (xml-dom) |
|
9 |
- (let ((root-node-name (make-keyword (string-upcase |
|
10 |
- ($ (inline xml-dom) (children) |
|
11 |
- (map #'tag-name) (node)))))) |
|
12 |
- (setf type |
|
13 |
- (case root-node-name |
|
14 |
- ((:feed) :atom) |
|
15 |
- (t root-node-name))))) |
|
6 |
+(defclass feed () |
|
7 |
+ ((title :initarg :title :initform nil) |
|
8 |
+ (link :initarg :link :initform nil) |
|
9 |
+ (items :initarg :items :initform nil) |
|
10 |
+ (description :initarg :description :initform nil) |
|
11 |
+ (feed-link :initarg :feed-link :initform nil) |
|
12 |
+ (doc :initarg :doc :initform nil) |
|
13 |
+ (source-type :initarg :source-type :initform nil))) |
|
14 |
+ |
|
15 |
+(defclass item () |
|
16 |
+ ((title :initarg :title :initform nil) |
|
17 |
+ (id :initarg :id :initform nil) |
|
18 |
+ (author :initarg :author :initform nil) |
|
19 |
+ (date :initarg :date :initform nil) |
|
20 |
+ (link :initarg :link :initform nil) |
|
21 |
+ (links :initform (make-hash-table :test #'equalp)) |
|
22 |
+ (content :initarg :content :initform nil) |
|
23 |
+ (doc :initarg :doc :initform nil))) |
|
24 |
+ |
|
25 |
+(define-condition duplicate-link-type (error) |
|
26 |
+ ((old :reader duplicate-link-type-old :initarg :old) |
|
27 |
+ (new :reader duplicate-link-type-new :initarg :new)) |
|
28 |
+ (:report (lambda (condition stream) |
|
29 |
+ (format stream "Item already has link ~s" (duplicate-link-type-old condition))))) |
|
16 | 30 |
|
17 | 31 |
|
18 | 32 |
(defgeneric push-item (feed item) |
19 | 33 |
(:documentation "Adds an item to the feed")) |
20 | 34 |
|
21 |
-(defgeneric make-item (xml-dom type)) |
|
35 |
+(defgeneric make-item (xml-dom doc-type) |
|
36 |
+ (:documentation "Given an xml document, return an item")) |
|
22 | 37 |
|
23 | 38 |
(defgeneric parse-feed (feed)) |
24 | 39 |
|
25 |
-(defgeneric %to-feed (doc type &key feed-link)) |
|
40 |
+(defgeneric %get-items (xml feed-type)) |
|
26 | 41 |
|
27 | 42 |
(defgeneric %generate-xml (feed feed-type &key partial)) |
43 |
+(defmethod %generate-xml :around ((feed feed) feed-type &rest r) |
|
44 |
+ (declare (ignore r)) |
|
45 |
+ (let ((result (call-next-method feed feed-type))) |
|
46 |
+ (with-slots (items) feed |
|
47 |
+ (loop for item in items |
|
48 |
+ do (%generate-xml item feed-type :partial result))) |
|
49 |
+ result)) |
|
50 |
+ |
|
51 |
+(defgeneric %to-feed (doc type &key feed-link) |
|
52 |
+ (:documentation "Given an xml-document, return a feed object")) |
|
53 |
+(defmethod %to-feed :around ((xml-dom plump:node) doc-type &key feed-link) |
|
54 |
+ "This wraps the particular methods so that _they_ don't have to implement item fetching. |
|
55 |
+ NIL passed to the type activates auto-detection" |
|
56 |
+ (aprog1 (call-next-method xml-dom doc-type :feed-link feed-link) |
|
57 |
+ (with-slots (doc source-type) it |
|
58 |
+ (setf doc xml-dom |
|
59 |
+ source-type doc-type)) |
|
60 |
+ (with-slots (items) it |
|
61 |
+ (setf |
|
62 |
+ items (loop for item across (%get-items xml-dom doc-type) |
|
63 |
+ collect (make-item item doc-type)))))) |
|
64 |
+ |
|
65 |
+(defgeneric (setf link) (value self)) |
|
66 |
+(defmethod (setf link) ((value cons) (self item)) |
|
67 |
+ (with-slots (links) self |
|
68 |
+ (destructuring-bind (type . href) value |
|
69 |
+ (when (consp href) |
|
70 |
+ (if (null (cdr href)) |
|
71 |
+ (setf href (car href)) |
|
72 |
+ (error 'type-error "too many arguments"))) |
|
73 |
+ (let ((type-keyword (make-keyword (string-upcase type)))) |
|
74 |
+ (when (slot-boundp self 'links) |
|
75 |
+ (multiple-value-bind (old-link old-link-p) (gethash type-keyword links) |
|
76 |
+ (when old-link-p |
|
77 |
+ (cerror "Replace Link ~a:~a with ~a:~a" 'duplicate-link-type :old old-link :new href)))) |
|
78 |
+ (setf (gethash type-keyword links) href))))) |
|
79 |
+ |
|
80 |
+(defmethod print-object ((object feed) stream) |
|
81 |
+ (print-unreadable-object (object stream :type t :identity t) |
|
82 |
+ (with-slots (title link) object |
|
83 |
+ (format stream "title: ~s link: ~s" |
|
84 |
+ (aif title (shorten-link it) "<untitled>") |
|
85 |
+ (aif link (shorten-link it) "<no link>"))))) |
|
86 |
+ |
|
87 |
+(defmethod print-object ((object item) stream) |
|
88 |
+ (print-unreadable-object (object stream :type t :identity t) |
|
89 |
+ (with-slots (title link date) object |
|
90 |
+ (format stream "title: ~s link: ~s date:~s" |
|
91 |
+ (aif title (shorten-link it) "<untitled>") |
|
92 |
+ (aif link (shorten-link it) "<no link>") |
|
93 |
+ (aif date it "<no date>"))))) |
|
94 |
+ |
|
95 |
+ |
|
96 |
+(defun detect-feed-type (xml-dom) |
|
97 |
+ (let ((root-node-name (make-keyword (string-upcase |
|
98 |
+ ($ (inline xml-dom) (children) |
|
99 |
+ (map #'tag-name) (node)))))) |
|
100 |
+ (setf type |
|
101 |
+ (case root-node-name |
|
102 |
+ ((:feed) :atom) |
|
103 |
+ (t root-node-name))))) |
|
104 |
+ |
|
28 | 105 |
|
29 | 106 |
(defun generate-xml (feed &key (feed-type :rss)) |
30 | 107 |
(%generate-xml feed feed-type)) |
... | ... |
@@ -37,8 +114,6 @@ |
37 | 114 |
(setf type (detect-feed-type doc))) |
38 | 115 |
(%to-feed doc type :feed-link feed-link)) |
39 | 116 |
|
40 |
-(defgeneric %get-items (xml feed-type) |
|
41 |
- (:method (xml-dom (feed-type (eql :rss))) ($ (inline xml-dom) "channel > item"))) |
|
42 | 117 |
|
43 | 118 |
(defun get-items (feed xml-dom &key type) |
44 | 119 |
(with-slots (items) feed |
... | ... |
@@ -46,18 +121,6 @@ |
46 | 121 |
do (push (make-item xml-dom type) items) |
47 | 122 |
finally (return items)))) |
48 | 123 |
|
49 |
-(defmethod %to-feed :around (xml-dom doc-type &key feed-link) |
|
50 |
- "This wraps the particular methods so that _they_ don't have to implement item fetching. |
|
51 |
- NIL passed to the type activates auto-detection" |
|
52 |
- (aprog1 (call-next-method xml-dom doc-type :feed-link feed-link) |
|
53 |
- (with-slots (doc source-type) it |
|
54 |
- (setf doc xml-dom |
|
55 |
- source-type doc-type)) |
|
56 |
- (with-slots (items) it |
|
57 |
- (setf |
|
58 |
- items (loop for item across (%get-items xml-dom doc-type) |
|
59 |
- collect (make-item item doc-type)))))) |
|
60 |
- |
|
61 | 124 |
(defgeneric feed-to-rss (feed)) |
62 | 125 |
(defgeneric feed-to-atom (feed)) |
63 | 126 |
(defgeneric feed-to-json (feed)) |
... | ... |
@@ -82,15 +145,6 @@ |
82 | 145 |
</body> |
83 | 146 |
</html>")) |
84 | 147 |
|
85 |
-(defclass feed () |
|
86 |
- ((title :initarg :title :initform nil) |
|
87 |
- (link :initarg :link :initform nil) |
|
88 |
- (items :initarg :items :initform nil) |
|
89 |
- (description :initarg :description :initform nil) |
|
90 |
- (feed-link :initarg :feed-link :initform nil) |
|
91 |
- (doc :initarg :doc :initform nil) |
|
92 |
- (source-type :initarg :source-type :initform nil))) |
|
93 |
- |
|
94 | 148 |
(defun make-feed (&key title link items feed-link description) |
95 | 149 |
(make-instance 'feed :title title :link link :items items :feed-link feed-link :description description)) |
96 | 150 |
|
... | ... |
@@ -105,135 +159,10 @@ |
105 | 159 |
(push-item feed it) |
106 | 160 |
(values feed it))) |
107 | 161 |
|
108 |
-(defmethod %generate-xml :around ((feed feed) feed-type &rest r) |
|
109 |
- (declare (ignore r)) |
|
110 |
- (let ((result (call-next-method feed feed-type))) |
|
111 |
- (with-slots (items) feed |
|
112 |
- (loop for item in items |
|
113 |
- do (%generate-xml item feed-type :partial result))) |
|
114 |
- result)) |
|
115 |
- |
|
116 |
-(defmethod shorten-link (link) |
|
162 |
+(defun shorten-link (link) |
|
117 | 163 |
(let ((link (cl-ppcre:regex-replace "^https?:" link ""))) |
118 | 164 |
(subseq link 0 (min 30 (length link))))) |
119 | 165 |
|
120 |
-(defmethod print-object ((object feed) stream) |
|
121 |
- (print-unreadable-object (object stream :type t :identity t) |
|
122 |
- (with-slots (title link) object |
|
123 |
- (format stream "title: ~s link: ~s" |
|
124 |
- (aif title (shorten-link it) "<untitled>") |
|
125 |
- (aif link (shorten-link it) "<no link>"))))) |
|
126 |
- |
|
127 |
-(defclass rss-feed (feed) ()) |
|
128 |
- |
|
129 |
-(defmethod %generate-xml ((feed rss-feed) (feed-type (eql :rss)) &rest r) |
|
130 |
- (declare (ignore r)) |
|
131 |
- (let* ((xml-root (plump:make-root)) |
|
132 |
- (feed-root (plump:make-element xml-root "rss")) |
|
133 |
- (channel (plump-dom:make-element feed-root "channel"))) |
|
134 |
- ($ (inline feed-root) |
|
135 |
- (attr "version" "2.0") |
|
136 |
- (attr "xmlns:content" "http://purl.org/rss/1.0/modules/content/") |
|
137 |
- (attr "xmlns:atom" "http://www.w3.org/2005/Atom")) |
|
138 |
- (with-slots (title link feed-link description) feed |
|
139 |
- ($ (inline (make-element channel "title")) |
|
140 |
- (text title)) |
|
141 |
- ($ (inline (make-element channel "link")) |
|
142 |
- (text link)) |
|
143 |
- (awhen description |
|
144 |
- ($ (inline (make-element channel "description")) |
|
145 |
- (text it))) |
|
146 |
- ($ (inline (make-element channel "atom:link")) |
|
147 |
- (attr "rel" "self") |
|
148 |
- (attr "type" "application/rss+xml") |
|
149 |
- (attr "href" link))) |
|
150 |
- xml-root)) |
|
151 |
- |
|
152 |
-(defclass item () |
|
153 |
- ((title :initarg :title :initform nil) |
|
154 |
- (id :initarg :id :initform nil) |
|
155 |
- (author :initarg :author :initform nil) |
|
156 |
- (date :initarg :date :initform nil) |
|
157 |
- (link :initarg :link :initform nil) |
|
158 |
- (links :initform (make-hash-table :test #'equalp)) |
|
159 |
- (content :initarg :content :initform nil) |
|
160 |
- (doc :initarg :doc :initform nil))) |
|
161 |
- |
|
162 |
-(defgeneric (setf link) (value self)) |
|
163 |
- |
|
164 |
-(define-condition duplicate-link-type (error) |
|
165 |
- ((old :reader duplicate-link-type-old :initarg :old) |
|
166 |
- (new :reader duplicate-link-type-new :initarg :new)) |
|
167 |
- (:report (lambda (condition stream) |
|
168 |
- (format stream "Item already has link ~s" (duplicate-link-type-old condition))))) |
|
169 |
- |
|
170 |
-(defmethod (setf link) ((value cons) (self item)) |
|
171 |
- (with-slots (links) self |
|
172 |
- (destructuring-bind (type . href) value |
|
173 |
- (when (consp href) |
|
174 |
- (if (null (cdr href)) |
|
175 |
- (setf href (car href)) |
|
176 |
- (error 'type-error "too many arguments"))) |
|
177 |
- (let ((type-keyword (make-keyword (string-upcase type)))) |
|
178 |
- (when (slot-boundp self 'links) |
|
179 |
- (multiple-value-bind (old-link old-link-p) (gethash type-keyword links) |
|
180 |
- (when old-link-p |
|
181 |
- (cerror "Replace Link ~a:~a with ~a:~a" 'duplicate-link-type :old old-link :new href)))) |
|
182 |
- (setf (gethash type-keyword links) href))))) |
|
183 |
- |
|
184 |
-(defmethod %generate-xml ((item item) (feed-type (eql :rss)) &key partial) |
|
185 |
- (prog1 partial |
|
186 |
- (let ((item-root (make-element ($ (inline partial) "channel" (node)) "item"))) |
|
187 |
- (with-slots (title id date link content) item |
|
188 |
- ($ (inline (make-element item-root "title")) (text title)) |
|
189 |
- ($ (inline (make-element item-root "link")) (text link)) |
|
190 |
- (plump-dom:set-attribute |
|
191 |
- ($ (inline (make-element item-root "guid")) (text id) (node)) |
|
192 |
- "isPermaLink" |
|
193 |
- "false") |
|
194 |
- ($ (inline (make-element item-root "pubDate")) (text date)) |
|
195 |
- ($ (inline (make-element item-root "description")) (text content)))))) |
|
196 |
- |
|
197 |
-(defmethod print-object ((object item) stream) |
|
198 |
- (print-unreadable-object (object stream :type t :identity t) |
|
199 |
- (with-slots (title link date) object |
|
200 |
- (format stream "title: ~s link: ~s date:~s" |
|
201 |
- (aif title (shorten-link it) "<untitled>") |
|
202 |
- (aif link (shorten-link it) "<no link>") |
|
203 |
- (aif date (shorten-link it) "<no date>"))))) |
|
204 |
- |
|
205 |
-;{{{ RSS feed handling |
|
206 |
-(defmethod make-item (xml-dom (type (eql :rss))) |
|
207 |
- (let* ((item-title ($ "> title" (text) (node))) |
|
208 |
- (item-link ($ "> link" (text) (node))) |
|
209 |
- (item-date ($ "> pubDate" (text) (node))) |
|
210 |
- (item-guid ($ "> guid" (text) (node))) |
|
211 |
- (item-description ($ "> description" (text) (node))) |
|
212 |
- (item-content-encoded ($ "> content::encoded" (text) (node))) |
|
213 |
- (content (with-output-to-string (s) |
|
214 |
- (serialize (parse (or item-content-encoded item-description)) s))) |
|
215 |
- (*tag-dispatchers* *html-tags*)) |
|
216 |
- (make-instance 'item |
|
217 |
- :content content |
|
218 |
- :date item-date |
|
219 |
- :doc xml-dom |
|
220 |
- :id item-guid |
|
221 |
- :link item-link |
|
222 |
- :title item-title))) |
|
223 |
- |
|
224 |
-(defmethod %to-feed (xml-dom (type (eql :rss)) &key feed-link) |
|
225 |
- ; TODO: store feed-link |
|
226 |
- (lquery:initialize xml-dom) |
|
227 |
- (let ((doc-title ($ "channel > title" (text) (node))) |
|
228 |
- (doc-link ($ "channel > link" (text) (node))) |
|
229 |
- (doc-feed-link (or feed-link |
|
230 |
- ($ "feed > atom::link[rel=self]" (first) (attr "href") (node))))) |
|
231 |
- (make-instance 'rss-feed :title doc-title :link doc-link :feed-link doc-feed-link))) |
|
232 |
-;}}} |
|
233 |
- |
|
234 |
-; {{{ ATOM feed handling |
|
235 |
- |
|
236 |
- |
|
237 | 166 |
(defun rdf-to-feed (xml-dom)) |
238 | 167 |
(defun json-to-feed (json-object)) |
239 | 168 |
(defun html5-to-feed (html-dom)) |
... | ... |
@@ -18,12 +18,13 @@ |
18 | 18 |
(make-instance 'atom-person :name name :uri uri :email email)) |
19 | 19 |
|
20 | 20 |
(defclass atom-feed (alimenta:feed) |
21 |
- ((subtitle :initarg :subtitle :initform nil) |
|
22 |
- (id :initarg :id :initform nil) |
|
23 |
- (icon :initarg :icon :initform nil) |
|
21 |
+ ((subtitle :initarg :subtitle :initform nil) |
|
22 |
+ (id :initarg :id :initform nil) |
|
23 |
+ (icon :initarg :icon :initform nil) |
|
24 | 24 |
(categories :initarg :categories :type (or null list) :initform nil) |
25 |
- (logo :initarg :logo :initform nil) |
|
26 |
- (authors :initarg :authors :type (or null list) :initform nil))) |
|
25 |
+ (logo :initarg :logo :initform nil) |
|
26 |
+ (updated :initarg :updated :initform nil) |
|
27 |
+ (authors :initarg :authors :type (or null list) :initform nil))) |
|
27 | 28 |
|
28 | 29 |
(defclass atom-item (alimenta:item) |
29 | 30 |
((author-uri :initarg :author-uri :initform nil))) |
... | ... |
@@ -64,7 +65,7 @@ |
64 | 65 |
(awhen (or item-content item-description) (serialize (parse it) s))))) |
65 | 66 |
(make-instance 'atom-item |
66 | 67 |
:content content |
67 |
- :date item-date |
|
68 |
+ :date (local-time:parse-timestring item-date) |
|
68 | 69 |
:id item-guid |
69 | 70 |
:author item-author |
70 | 71 |
:author-uri item-author-uri |
... | ... |
@@ -90,6 +91,7 @@ |
90 | 91 |
(doc-icon (get-feed-elem "feed > icon")) |
91 | 92 |
(doc-logo (get-feed-elem "feed > logo")) |
92 | 93 |
(doc-id (get-feed-elem "feed > id")) |
94 |
+ (doc-updated (awhen (get-feed-elem "feed > updated") (local-time:parse-timestring it))) |
|
93 | 95 |
(doc-link (get-feed-elem-attr "feed > link[rel=alternate]" "href")) |
94 | 96 |
(doc-feed-link (or feed-link (get-feed-elem-attr "feed > link[rel=self]" "href"))) |
95 | 97 |
(doc-categories ($ (inline xml-dom) "feed > category" |
... | ... |
@@ -102,6 +104,7 @@ |
102 | 104 |
:icon doc-icon |
103 | 105 |
:logo doc-logo |
104 | 106 |
:link doc-link |
107 |
+ :updated doc-updated |
|
105 | 108 |
:id doc-id |
106 | 109 |
:feed-link doc-feed-link |
107 | 110 |
:subtitle doc-subtitle |
... | ... |
@@ -134,7 +137,7 @@ |
134 | 137 |
(plump:make-element (plump:make-root) "feed")))) |
135 | 138 |
(prog1 parent |
136 | 139 |
(let ((feed-root (make-element parent "feed"))) |
137 |
- (with-slots (title id link feed-link description) feed |
|
140 |
+ (with-slots (title id updated link feed-link description) feed |
|
138 | 141 |
($ (inline (make-element feed-root "title")) (text title) |
139 | 142 |
|
140 | 143 |
(inline (make-element feed-root "link")) |
... | ... |
@@ -145,6 +148,7 @@ |
145 | 148 |
|
146 | 149 |
(inline (make-element feed-root "id")) (text id) (node) |
147 | 150 |
(inline (make-element feed-root "summary")) (text description) (node) |
151 |
+ (inline (make-element feed-root "updated")) (text updated) (node) |
|
148 | 152 |
)))))) |
149 | 153 |
|
150 | 154 |
|
... | ... |
@@ -161,7 +165,9 @@ |
161 | 165 |
(inline (make-element item-root "author")) |
162 | 166 |
(append ($ (inline (make-element item-root "name")) (text author))) |
163 | 167 |
(append ($ (inline (make-element item-root "uri")) (text author-uri))) |
164 |
- (inline (make-element item-root "content")) (text content))))))) |
|
168 |
+ (inline (make-element item-root "content")) (text content) |
|
169 |
+ (inline (make-element item-root "updated")) (text date) (node) |
|
170 |
+ )))))) |
|
165 | 171 |
|
166 | 172 |
|
167 | 173 |
(defconstants-really |
... | ... |
@@ -6,6 +6,9 @@ |
6 | 6 |
#:feed #:title #:link #:items #:feed-link #:doc #:source-type #:id #:date #:content |
7 | 7 |
#:item #:description #:%generate-xml #:%to-feed #:%get-items #:make-item)) |
8 | 8 |
|
9 |
+(defpackage #:alimenta.rss |
|
10 |
+ (:use #:cl #:should-test #:lquery #:plump #:alexandria #:anaphora #:alimenta)) |
|
11 |
+ |
|
9 | 12 |
(defpackage #:alimenta.atom |
10 | 13 |
(:use #:cl #:should-test #:lquery #:plump #:alexandria #:anaphora #:alimenta)) |
11 | 14 |
|
12 | 15 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,73 @@ |
1 |
+(declaim (optimize (speed 0) (safety 3) (debug 3))) |
|
2 |
+ |
|
3 |
+(in-package :alimenta.rss) |
|
4 |
+ |
|
5 |
+(defclass rss-feed (feed) ()) |
|
6 |
+(defclass rss-item (feed) ()) |
|
7 |
+ |
|
8 |
+(defmethod %get-items (xml-dom (feed-type (eql :rss))) |
|
9 |
+ ($ (inline xml-dom) "channel > item")) |
|
10 |
+ |
|
11 |
+(defmethod %generate-xml ((item item) (feed-type (eql :rss)) &key partial) |
|
12 |
+ (prog1 partial |
|
13 |
+ (let ((item-root (make-element ($ (inline partial) "channel" (node)) "item"))) |
|
14 |
+ (with-slots (title id date link content) item |
|
15 |
+ ($ (inline (make-element item-root "title")) (text title)) |
|
16 |
+ ($ (inline (make-element item-root "link")) (text link)) |
|
17 |
+ (plump-dom:set-attribute |
|
18 |
+ ($ (inline (make-element item-root "guid")) (text id) (node)) |
|
19 |
+ "isPermaLink" |
|
20 |
+ "false") |
|
21 |
+ ($ (inline (make-element item-root "pubDate")) (text date)) |
|
22 |
+ ($ (inline (make-element item-root "description")) (text content)))))) |
|
23 |
+ |
|
24 |
+(defmethod %generate-xml ((feed feed) (feed-type (eql :rss)) &rest r) |
|
25 |
+ (declare (ignore r)) |
|
26 |
+ (let* ((xml-root (plump:make-root)) |
|
27 |
+ (feed-root (plump:make-element xml-root "rss")) |
|
28 |
+ (channel (plump-dom:make-element feed-root "channel"))) |
|
29 |
+ ($ (inline feed-root) |
|
30 |
+ (attr "version" "2.0") |
|
31 |
+ (attr "xmlns:content" "http://purl.org/rss/1.0/modules/content/") |
|
32 |
+ (attr "xmlns:atom" "http://www.w3.org/2005/Atom")) |
|
33 |
+ (with-slots (title link feed-link description) feed |
|
34 |
+ ($ (inline (make-element channel "title")) |
|
35 |
+ (text title)) |
|
36 |
+ ($ (inline (make-element channel "link")) |
|
37 |
+ (text link)) |
|
38 |
+ (awhen description |
|
39 |
+ ($ (inline (make-element channel "description")) |
|
40 |
+ (text it))) |
|
41 |
+ ($ (inline (make-element channel "atom:link")) |
|
42 |
+ (attr "rel" "self") |
|
43 |
+ (attr "type" "application/rss+xml") |
|
44 |
+ (attr "href" link))) |
|
45 |
+ xml-root)) |
|
46 |
+ |
|
47 |
+(defmethod make-item (xml-dom (type (eql :rss))) |
|
48 |
+ (let* ((item-title ($ "> title" (text) (node))) |
|
49 |
+ (item-link ($ "> link" (text) (node))) |
|
50 |
+ (item-date ($ "> pubDate" (text) (node))) |
|
51 |
+ (item-guid ($ "> guid" (text) (node))) |
|
52 |
+ (item-description ($ "> description" (text) (node))) |
|
53 |
+ (item-content-encoded ($ "> content::encoded" (text) (node))) |
|
54 |
+ (content (with-output-to-string (s) |
|
55 |
+ (serialize (parse (or item-content-encoded item-description)) s))) |
|
56 |
+ (*tag-dispatchers* *html-tags*)) |
|
57 |
+ (make-instance 'item |
|
58 |
+ :content content |
|
59 |
+ :date item-date |
|
60 |
+ :doc xml-dom |
|
61 |
+ :id item-guid |
|
62 |
+ :link item-link |
|
63 |
+ :title item-title))) |
|
64 |
+ |
|
65 |
+(defmethod %to-feed (xml-dom (type (eql :rss)) &key feed-link) |
|
66 |
+ ; TODO: store feed-link |
|
67 |
+ (lquery:initialize xml-dom) |
|
68 |
+ (let ((doc-title ($ "channel > title" (text) (node))) |
|
69 |
+ (doc-link ($ "channel > link" (text) (node))) |
|
70 |
+ (doc-feed-link (or feed-link |
|
71 |
+ ($ "feed > atom::link[rel=self]" (first) (attr "href") (node))))) |
|
72 |
+ (make-instance 'rss-feed :title doc-title :link doc-link :feed-link doc-feed-link))) |
|
73 |
+ |
... | ... |
@@ -4,20 +4,6 @@ |
4 | 4 |
(ql:quickload :spinneret) |
5 | 5 |
(ql:quickload :lass) |
6 | 6 |
|
7 |
-(defmethod araneus:view ((name (eql 'root)) (item alimenta:item)) |
|
8 |
- (with-slots ((title alimenta:title) (link alimenta:link)) item |
|
9 |
- (spinneret:with-html |
|
10 |
- (:article |
|
11 |
- (:div.title title) |
|
12 |
- (:a.link :href link link))))) |
|
13 |
- |
|
14 |
-(defmethod araneus:view ((name (eql 'root)) (feed alimenta:feed)) |
|
15 |
- (with-slots ((title alimenta:title) (link alimenta:link)) feed |
|
16 |
- (spinneret:with-html |
|
17 |
- (:header |
|
18 |
- (:h1.feed-title title) |
|
19 |
- (:a.feed-link link))))) |
|
20 |
- |
|
21 | 7 |
(defun get-css () |
22 | 8 |
(lass:compile-and-write |
23 | 9 |
`(* |
... | ... |
@@ -45,6 +31,20 @@ |
45 | 31 |
) |
46 | 32 |
)) |
47 | 33 |
|
34 |
+ (defmethod araneus:view ((name (eql 'root)) (item alimenta:item)) |
|
35 |
+ (with-slots ((title alimenta:title) (link alimenta:link)) item |
|
36 |
+ (spinneret:with-html |
|
37 |
+ (:article |
|
38 |
+ (:div.title title) |
|
39 |
+ (:a.link :href link link))))) |
|
40 |
+ |
|
41 |
+(defmethod araneus:view ((name (eql 'root)) (feed alimenta:feed)) |
|
42 |
+ (with-slots ((title alimenta:title) (link alimenta:link)) feed |
|
43 |
+ (spinneret:with-html |
|
44 |
+ (:header |
|
45 |
+ (:h1.feed-title title) |
|
46 |
+ (:a.feed-link link))))) |
|
47 |
+ |
|
48 | 48 |
(defmethod araneus:view :around ((name (eql 'root)) (feed alimenta:feed)) |
49 | 49 |
(with-slots ((title alimenta:title) (items alimenta::items)) feed |
50 | 50 |
(spinneret:with-html-string |