git.fiddlerwoaroof.com
Browse code

Add sbcl support

fiddlerwoaroof authored on 15/02/2017 09:45:15
Showing 6 changed files
... ...
@@ -1,2 +1,6 @@
1
-all:
1
+all: ccl
2
+
3
+sbcl:
2 4
 	sbcl --load deploy.lisp
5
+ccl:
6
+	ccl --load deploy.lisp
... ...
@@ -1,4 +1,8 @@
1 1
 (load (truename "~/quicklisp/setup.lisp"))
2 2
 (push "/home/edwlan/github_repos/alimenta-feed-archive/" asdf:*central-registry*)
3 3
 (ql:quickload :alimenta-feed-archive)
4
-(save-lisp-and-die "feed-archiver" :executable t :toplevel #'alimenta.feed-archive::command-line-main)
4
+(#+ccl ccl:save-application
5
+ #+sbcl save-lisp-and-die
6
+ "feed-archiver"
7
+ #+sbcl :executable  #+ccl :prepend-kernel t
8
+ #+sbcl :toplevel #+ccl :toplevel-function #'alimenta.feed-archive::command-line-main)
... ...
@@ -9,19 +9,19 @@
9 9
   ;; Work around some issue with pathnames
10 10
   (setf path (merge-pathnames path (make-pathname :type :unspecific))))
11 11
 
12
-(defun %encode-item (root-dir item)
13
-  (destructuring-bind (title path) item
14
-    (format t "~&Encoding ~a~%" title)
15
-    (restart-once (fix-pathname () (fix-path path))
16
-      (let ((pathname (uiop:enough-pathname path root-dir)))
17
-	(yason:with-object ()
18
-	  (yason:encode-object-element "title" title)
19
-	  (yason:encode-object-element "path" pathname))))))
20
-
21 12
 (defun skip-item ()
22 13
   (when-let ((restart (find-restart 'skip-item)))
23 14
     (invoke-restart restart)))
24 15
 
16
+(define-condition feed-error (error)
17
+  ((%feed :reader the-feed :initarg :feed :initform (error "We need a feed"))
18
+   (%condition :reader the-condition :initarg :condition :initform (error "feed-error must wrap a condition"))))
19
+
20
+(defun wrap-condition (condition feed)
21
+  (error 'feed-error
22
+	 :feed feed
23
+	 :condition condition))
24
+
25 25
 (defun %encode-feed-as-json (feed item-storage-info root-dir &optional stream)
26 26
   (with-accessors ((description alimenta:description)
27 27
 		   (feed-link alimenta:feed-link)
... ...
@@ -35,22 +35,32 @@
35 35
       (yason:with-object-element ("items")
36 36
 	(yason:with-array ()
37 37
 	  (dolist (item item-storage-info)
38
+            
38 39
 	    (with-simple-restart (skip-item "Skip item ~s" (car item))
39
-	      (%encode-item root-dir item))))))))
40
+              ;; (format t "~&I Store Info: ~a~%~4t~a~%" (uiop:unix-namestring (cadr item)) root-dir)
41
+	      (%encode-item root-dir item)
42
+	      #+null
43
+              (yason:encode-array-element (uiop:unix-namestring (uiop:enough-pathname root-dir (cadr item))))
44
+	      )))))))
40 45
 
41
-(defmethod store ((item alimenta:item) (directory pathname))
42
-  (flet ((get-item-store-name (item directory)
43
-	   (let ((id (get-id item)))
44
-	     (merge-pathnames (make-pathname :name id) directory))))
45
-
46
-    (prog1-let ((item-title (alimenta:title item))
47
-		(fn (get-item-store-name item directory)))
48
-      (with-open-file (item-f fn :direction :output)
49
-	(yason:encode item item-f)))))
46
+(defmethod store ((items sequence) (directory pathname))
47
+  (map 'list (lambda (item) (store item directory))
48
+       (stable-sort
49
+	(sort (remove-if #'older-than-a-week items :key #'alimenta:date)
50
+	      #'string-lessp
51
+	      :key #'alimenta:title)
52
+	#'local-time:timestamp>
53
+	:key #'alimenta:date)))
50 54
 
51
-(define-condition feed-error (error)
52
-  ((%feed :initarg :feed :initform (error "We need a feed"))
53
-   (%condition :initarg :condition :initform (error "feed-error must wrap a condition"))))
55
+(defun sort-and-filter-items (feed)
56
+  (setf (alimenta:items feed)
57
+	(stable-sort
58
+	 (sort (remove-if #'older-than-a-week (alimenta:items feed)
59
+			  :key #'alimenta:date)
60
+	       #'string-lessp
61
+	       :key #'alimenta:title)
62
+	 #'local-time:timestamp>
63
+	 :key #'alimenta:date)))
54 64
 
55 65
 (defmethod store ((feed alimenta:feed) (directory pathname))
56 66
   (flet ((get-feed-store-name (feed directory)
... ...
@@ -63,6 +73,7 @@
63 73
 		     (link alimenta:link)
64 74
 		     (source-type alimenta:source-type)
65 75
 		     (title alimenta:title)) feed
76
+      ; We wrap all errors with our own condition
66 77
       (handler-bind ((error (lambda (c) (error 'feed-error :feed feed :condition c))))
67 78
 	(prog1-let ((feed-title title)
68 79
 		    (feed-store (get-feed-store-name feed directory)))
... ...
@@ -73,12 +84,40 @@
73 84
 				  feed-store
74 85
 				  index)))))))
75 86
 
76
-(defmethod store ((items sequence) (directory pathname))
77
-  (map 'list (lambda (item) (store item directory))
78
-       (stable-sort
79
-	(sort (remove-if #'older-than-a-week items :key #'alimenta:date)
80
-	      #'string-lessp
81
-	      :key #'alimenta:title)
82
-	#'local-time:timestamp>
83
-	:key #'alimenta:date)))
87
+(defmethod store ((feed alimenta:feed) (stream stream))
88
+  (handler-bind ((error (lambda (c)
89
+			  (typecase c
90
+			    (feed-error c)
91
+			    (t (wrap-condition c feed))))))
92
+    (yason:with-output (stream :indent t)
93
+      (yason:with-object ()
94
+	(yason:with-object-element ("metadata")
95
+	  (yason:encode-object feed))
96
+	(yason:with-object-element ("items")
97
+	  (yason:with-array ()
98
+	    (for:for ((item over feed))
99
+	      (store item stream))))))))
100
+
101
+(defun %encode-item (root-dir item)
102
+  (destructuring-bind (title path) item
103
+    (format t "~&Encoding ~a~%" title)
104
+    (restart-once (fix-pathname () (fix-path path))
105
+      (let ((pathname (uiop:enough-pathname path root-dir)))
106
+	(yason:with-object ()
107
+	  (yason:encode-object-element "title" title)
108
+	  (yason:encode-object-element "path" pathname))))))
109
+
110
+(defmethod store ((item alimenta:item) (directory pathname))
111
+  (flet ((get-item-store-name (item directory)
112
+	   (let ((id (get-id item)))
113
+	     (merge-pathnames (make-pathname :name id :version nil :type "json") directory))))
114
+
115
+    (prog1-let ((item-title (alimenta:title item))
116
+		(fn (get-item-store-name item directory)))
117
+      (with-open-file (item-f fn :direction :output)
118
+	(yason:encode item item-f)))))
119
+
120
+(defmethod store ((item alimenta:item) (stream stream))
121
+  (yason:with-output (stream :indent t)
122
+    (yason:encode-object item)))
84 123
 
... ...
@@ -21,8 +21,8 @@
21 21
 
22 22
 (defun test-feed-list ()
23 23
   (values '("http://feeds.feedburner.com/GamasutraFeatureArticles/"
24
-	    "http://feeds.feedburner.com/GamasutraNews/"
25
-	    "http://feeds.feedburner.com/GamasutraColumns/")
24
+	    "https://www.codinghorror.com/blog/index.xml"
25
+	    "https://sancrucensis.wordpress.com/feed/")
26 26
 	  #p"/tmp/feed-archive/"))
27 27
 
28 28
 (defun init-feeds (&key feed-list archive-root)
... ...
@@ -40,8 +40,9 @@
40 40
 	   :test #'equalp))
41 41
 
42 42
 (defun safe-pull-feed (feed-url)
43
-  "Handles date parsing errors in the feed: chronicity won't parse certain date formats, this catches the error 
44
-and modifies the format to something chronicity can handle."
43
+  "Handles date parsing errors in the feed: chronicity won't parse
44
+   certain date formats, this catches the error and modifies the
45
+   format to something chronicity can handle."
45 46
   (let ((pop-times 0))
46 47
     (flet ((pop-50-tokens (c)
47 48
 	     (declare (ignore c))
... ...
@@ -52,24 +53,31 @@ and modifies the format to something chronicity can handle."
52 53
 				  pop-times)
53 54
 			  (alimenta.rss::pop-token))
54 55
 		   (continue)))))
55
-      (handler-bind ((error #'pop-50-tokens))
56
+      (handler-bind ((warning #'muffle-warning)
57
+		     (error #'pop-50-tokens))
58
+	(format t "~&Tring to pull: ~a... " feed-url)
56 59
 	(prog1 (alimenta.pull-feed:pull-feed feed-url)
57 60
 	  ;; Why am I decf-ing here?
61
+	  (format t "... Success~%" feed-url)
58 62
 	  (decf pop-times))))))
59 63
 
60 64
 (defun skip-feed ()
61 65
   (when-let ((restart (find-restart 'skip-feed)))
62 66
     (invoke-restart restart)))
63 67
 
68
+(defun pull-and-store-feeds (feeds pull-directory)
69
+  (loop for feed-url in feeds
70
+     collect
71
+       (with-simple-restart (skip-feed "Skip ~a" feed-url)
72
+	 (let ((feed (safe-pull-feed feed-url)))
73
+	   (setf (alimenta:feed-link feed)
74
+		 feed-url)
75
+	   (store feed pull-directory)))))
76
+
64 77
 (defun archive-feeds ()
65 78
   (let ((pull-time (local-time:now)))
66 79
     (let* ((pull-directory (get-store-directory-name pull-time)) 
67
-	   (paths (loop for feed-url in *feeds* collect
68
-		       (with-simple-restart (skip-feed "Skip ~a" feed-url)
69
-			 (let ((feed (safe-pull-feed feed-url)))
70
-			   (setf (alimenta:feed-link feed)
71
-				 feed-url)
72
-			   (store feed pull-directory))))))
80
+	   (paths (pull-and-store-feeds *feeds* pull-directory)))
73 81
       (with-open-file (index (merge-pathnames "index.json" pull-directory) :direction :output)
74 82
 	(yason:with-output (index :indent t)
75 83
 	  (yason:with-object ()
... ...
@@ -90,16 +98,33 @@ and modifies the format to something chronicity can handle."
90 98
 			paths)))))))))
91 99
 
92 100
 
101
+;; This is an ungodly mess, we need to avoid funneling everything through fix-pathname-or-skip
93 102
 (defun command-line-main (&optional (feed-list-initializer #'init-feeds))
94
-  (flet ((fix-pathname-or-continue (c)
95
-	   (declare (ignorable c))
96
-	   (format t "~&Received condition ~s~%" c)
97
-	   (if (find-restart 'fix-pathname)
98
-	       (progn (fix-pathname)
99
-		      (format t "~&Fixing pathname...~%"))
100
-	       (progn (format t "~&Skipping a feed...~%")
101
-		      (continue)))))
102
-    (handler-bind ((error (lambda (c) (fix-pathname-or-continue c))))
103
+  (declare (optimize (debug 3)))
104
+  (labels ((feed-type-unsupported (c &key (restart 'skip-feed))
105
+	     (format t "~&Feed type unsupported: ~a for feed ~a~%"
106
+		     (alimenta:feed-type c)
107
+		     (alimenta:feed-link c))
108
+	     (funcall restart))
109
+	   (fix-pathname-or-skip (c &key (restart 'skip-feed) (wrapped-condition nil wc-p))
110
+	     (let ((wrapped-condition (or wrapped-condition c)))
111
+	       (typecase wrapped-condition
112
+		 (alimenta:feed-type-unsupported (feed-type-unsupported))
113
+		 (otherwise
114
+		  (if (find-restart 'fix-pathname)
115
+		      (progn (fix-pathname)
116
+			     (format t "~&Fixing pathname...~%"))
117
+		      (progn (unless (eq restart 'continue)
118
+			       (format t "~&Skipping a feed... ~s~%"
119
+				       (if wc-p
120
+					   (alimenta.feed-archive.encoders::the-feed c)
121
+					   "Unknown")))
122
+			     (funcall restart))))))))
123
+
124
+    (handler-bind ((alimenta.feed-archive.encoders::feed-error
125
+		    (lambda (c) (fix-pathname-or-skip c :wrapped-condition (alimenta.feed-archive.encoders::the-condition c))))
126
+		   (alimenta:feed-type-unsupported #'feed-type-unsupported)
127
+		   (error (lambda (c) (fix-pathname-or-skip c :restart 'continue))))
103 128
       (multiple-value-bind (*feeds* *feed-base*) (funcall feed-list-initializer)
104 129
 	(alimenta.pull-feed::with-user-agent ("Feed Archiver v0.1b")
105 130
 	  (archive-feeds))))))
... ...
@@ -50,7 +50,7 @@
50 50
 	       (local-time:format-timestring nil (alimenta:date item))
51 51
 	       "-"
52 52
 	       (sha256-string (alimenta:id item))
53
-	       ".json"))
53
+	       #+nil ".json"))
54 54
 
55 55
 (defun older-than-a-week (date)
56 56
   (let ((week-ago (local-time:timestamp- (local-time:now)
... ...
@@ -28,7 +28,7 @@
28 28
   (yason:with-output (stream :indent t)
29 29
     (yason:encode-object feed)))
30 30
 
31
-(defmethod yason:encode ((item alimenta:item) &optional stream)
31
+(defmethod yason:encode-slots progn ((item alimenta:item))
32 32
   (with-accessors ((author alimenta::author)
33 33
 		   (content alimenta:content)
34 34
 		   (date alimenta:date)
... ...
@@ -36,13 +36,16 @@
36 36
 		   (link alimenta:link)
37 37
 		   (title alimenta:title)) item
38 38
     (let* ((date (local-time:format-timestring nil date)))
39
-      (yason:with-output (stream :indent t)
40
-	(yason:with-object ()
41
-	  (yason:encode-object-element "title" title)
42
-	  (yason:encode-object-element "date" date)
43
-	  (yason:encode-object-element "author" title)
44
-	  (yason:encode-object-element "id" (princ-to-string id))
45
-	  (yason:encode-object-element "link" link)
46
-	  (yason:encode-object-element "content" content)))))
39
+      (yason:with-object ()
40
+	(yason:encode-object-element "title" title)
41
+	(yason:encode-object-element "date" date)
42
+	(yason:encode-object-element "author" title)
43
+	(yason:encode-object-element "id" (princ-to-string id))
44
+	(yason:encode-object-element "link" link)
45
+	(yason:encode-object-element "content" content)))))
46
+
47
+(defmethod yason:encode ((item alimenta:item) &optional stream)
48
+  (yason:with-output (stream :indent t)
49
+    (yason:encode-slots item))
47 50
   item)
48 51