git.fiddlerwoaroof.com
Browse code

saving work ...

fiddlerwoaroof authored on 23/05/2016 20:44:22
Showing 2 changed files
... ...
@@ -10,9 +10,10 @@
10 10
 (ql:quickload :spinneret)
11 11
 (ql:quickload :clack)
12 12
 (ql:quickload :clack-errors)
13
+(ql:quickload :cl-markdown)
13 14
 
14 15
 (defpackage :blogerate
15
-  (:use :cl :alexandria :ningle :anaphora :ningle))
16
+  (:use :cl :alexandria :ningle :anaphora :ningle :spinneret))
16 17
 
17 18
 (in-package :blogerate)
18 19
 (ubiquitous:restore 'blogerate)
... ...
@@ -20,18 +21,30 @@
20 21
 (manardb:use-mmap-dir "/tmp/manardb-blogerate")
21 22
 
22 23
 (manardb:defmmclass blog ()
23
-    ((id :initarg :id :accessor blog-id)
24
-     (posts :initarg :posts :accessor blog-posts :initform nil)
25
-     (tags :initform (make-hash-table :test #'equal) :accessor blog-tags)
26
-     (authors :initform (make-hash-table :test #'equal) :accessor blog-authors)
27
-     (sets :initform (make-hash-table :test #'equal) :accessor blog-sets)))
24
+  ((id :initarg :id :accessor blog-id)
25
+   (posts :initarg :posts :accessor blog-posts :initform nil)
26
+   (tags :initform nil :accessor blog-tags)
27
+   (authors :initform nil :accessor blog-authors)
28
+   (sets :initform nil :accessor blog-sets)
29
+   (seed :initarg :seed
30
+         :initform (make-array 10
31
+                               :element-type '(unsigned-byte 8)
32
+                               :initial-contents (loop for x from 1 to 10 collect (random 256)))
33
+         :accessor blog-seed)))
34
+
35
+(manardb:defmmclass post-set ()
36
+  ((id :initarg :id :accessor set-id :initform nil)
37
+   (name :initarg :name :accessor set-name)
38
+   (posts :initarg :posts :accessor set-posts :initform nil)
39
+   (seed :initarg :seed
40
+         :initform (make-array 10
41
+                               :element-type '(unsigned-byte 8)
42
+                               :initial-contents (loop for x from 1 to 10 collect (random 256)))
43
+         :accessor set-seed)))
28 44
 
29 45
 (manardb:defmmclass post ()
30 46
     ((id :initarg :id :accessor post-id :initform (with-output-to-string (s)
31
-                                                    (uuid:print-bytes s
32
-                                                                      (uuid:make-v5-uuid
33
-                                                                        uuid:+namespace-oid+
34
-                                                                        "blump"))))
47
+                                                    (uuid:print-bytes s (uuid:make-v4-uuid))))
35 48
      (title :initarg :title :accessor post-title :initform "")
36 49
      (text :initarg :text :accessor post-text :initform "")
37 50
      (author :initarg :author :accessor post-author :initform "")
... ...
@@ -39,15 +52,44 @@
39 52
      (tags :initarg :tags :initform '() :accessor post-tags)))
40 53
 
41 54
 (defun make-post (blog title text author set &key (tags nil tags-p))
42
-  (let ((result (make-instance 'post :title title :text text :author author :set set :tags tags)))
55
+  (let ((result (make-instance 'post :title title :text text :author author :set set :tags tags))
56
+        (set-obj (get-set :name set :author author)))
43 57
     (push result (blog-posts blog))
44 58
     (when tags-p
45 59
       (mapcar (lambda (x) (push (cons x result) (blog-tags blog)))
46 60
               (post-tags result)))
47
-    (push (cons (post-set result) result) (blog-sets blog))
61
+    (push result (set-posts set-obj))
62
+    (unless (member set (blog-sets blog) :key #'car :test #'string=)
63
+      (push (cons set set-obj) (blog-sets blog)))
48 64
     (push (cons (post-author result) result) (blog-authors blog))
49 65
     result))
50 66
 
67
+(defparameter *blog* (get-blog "my-blog9"))
68
+(defvar *app* (make-instance 'ningle:<app>))
69
+
70
+
71
+(defun get-set-by-id (id &optional (blog *blog*))
72
+  (awhen (blog-sets blog)
73
+    (awhen (assoc id it :test #'string=)
74
+      (cdr it))))
75
+
76
+(defun get-set-by-name-and-author (name author)
77
+  (flet ((new-set ()
78
+           (let ((new-set (make-instance 'post-set :name name)))
79
+             (setf (set-id new-set) (get-set-id (set-seed new-set) name author))
80
+             new-set)))
81
+    (aif (manardb:retrieve-all-instances 'post-set)
82
+      (aif (car (remove-if-not (lambda (x) (equal (get-set-id (set-seed x) name author) (set-id x)))
83
+                               it))
84
+        it
85
+        (new-set))
86
+      (new-set))))
87
+
88
+(defun get-set (&key id name author)
89
+  (if id
90
+    (get-set-by-id id)
91
+    (get-set-by-name-and-author name author)))
92
+
51 93
 (defun get-blog (id)
52 94
   (flet ((new-blog () (make-instance 'blog :id id)))
53 95
     (aif (manardb:retrieve-all-instances 'blog)
... ...
@@ -56,9 +98,6 @@
56 98
         (new-blog))
57 99
       (new-blog))))
58 100
 
59
-(defparameter *blog* (get-blog "my-blog3"))
60
-(defparameter *app* (make-instance 'ningle:<app>))
61
-
62 101
 (defmacro with-page ((&key title) &body body)
63 102
   `(spinneret:with-html-string
64 103
      (:doctype)
... ...
@@ -66,67 +105,141 @@
66 105
        (:head
67 106
          (:link :rel "stylesheet" :href "/static/main.css")
68 107
          (:title ,title))
69
-       (:body ,@body))))
108
+       (:body
109
+         (:main
110
+           ,@body)))))
111
+
112
+(defmacro format-post (item)
113
+  (alexandria:once-only (item)
114
+    `(spinneret:with-html
115
+       (:section :class "post-show"
116
+        (:h2
117
+          (post-title ,item))
118
+        (:article
119
+          (:raw
120
+            (nth-value 1 (cl-markdown:markdown (post-text ,item) :stream nil))))
121
+        (:span :class "author"
122
+         (post-author ,item))
123
+        (:span :class "set"
124
+         (post-set ,item))
125
+        (:ul.tags
126
+          (loop for tag in (post-tags ,item)
127
+                collect (:li tag)))))))
128
+
129
+(defun get-set-posts (set-name)
130
+  (car
131
+    (loop for (set . post) in (blog-sets *blog*)
132
+          if (string= set set-name)
133
+          collect post)))
134
+
135
+(defun get-tag (tag-name)
136
+  (mapcar #'cdr
137
+          (remove-if-not (lambda (x) (string= x tag-name))
138
+                         (blog-tags *blog*)
139
+                         :key #'car)))
140
+
141
+(setf (route *app* "/posts/:set" :method :GET)
142
+      (lambda (params)
143
+        (declare (ignorable params))
144
+        (let* ((set-name (cdr (assoc :set params)))
145
+               (set-title (format nil "Set ~a" set-name))
146
+               (set (get-set :id set-name)))
147
+          (format nil "~s" (get-set :id set-name))
148
+          (with-page (:title set-title)
149
+            (:header
150
+              (:h1 (set-name set)))
151
+            (loop for post in (set-posts set)
152
+                  collect (format nil "~s" (format-post post)))))))
70 153
 
154
+(setf (route *app* "/posts/:set/:tag" :method :GET)
155
+      (lambda (params)
156
+        (let* ((set-id (cdr (assoc :set params)))
157
+               (tag-name (cdr (assoc :tag params)))
158
+               (set (get-set :id set-id))
159
+               (set-posts (set-posts set))
160
+               (tag-posts (get-tag tag-name))
161
+               (display-posts (intersection set-posts tag-posts
162
+                                            :key #'post-id
163
+                                            :test #'string=)))
164
+          (with-page (:title (format nil "Set: ~a Tag: ~a" (set-name set) tag-name))
165
+            (:header (:h1 (format nil "Set: ~a Tag: ~a" (set-name set) tag-name)))
166
+            (mapcar (lambda (post) (format nil "~s" (format-post post)))
167
+                    display-posts)))))
168
+
169
+(spinneret:deftag form-input (body attrs &key name label (type "text"))
170
+  (declare (ignore body))
171
+  (alexandria:once-only (name)
172
+    `(:div.input-group :class ,name ,@attrs
173
+       ;(:label :for ,name ,label)
174
+       (:input :placeholder ,label :type ,type :name ,name))))
71 175
 
72 176
 (setf (route *app* "/post/new" :method :GET)
73 177
       (lambda (params)
74 178
         (declare (ignorable params))
75
-        (with-page (:title "new post")
76
-          (:header
77
-            (:h1 "New Post:"))
78
-          (:section :class "post-new"
79
-            (:form :action "/post/new" :method "POST"
80
-             (:input :type "text" :name "title")
81
-             (:input :type "text" :name "author")
82
-             (:textarea :name "post")
83
-             (:input :type "text" :name "set")
84
-             (:input :type "text" :name "tags")
85
-             (:input :type "submit"))))))
179
+          (with-page (:title "new post")
180
+            (:header
181
+              (:h1 "New Post:"))
182
+            (:section :class "post-new"
183
+              (:form :action "/post/new" :method "POST"
184
+               (form-input :name "title" :label "Title")
185
+               (form-input :name "author" :label "Author")
186
+               (:div.input-group.post
187
+                 ;(:label :for "post" "Post Content")
188
+                 (:textarea :placeholder "Content" :name "post"))
189
+               (form-input :name "set" :label "Set")
190
+               (form-input :name "tags" :label "Comma-separated Tags")
191
+               (form-input :type "submit"))))))
86 192
 
87 193
 (defun str-assoc-cdr (key alist)
88 194
   (cdr (assoc key alist :test #'equal)))
89 195
 
90 196
 (defun split-tags (str)
91
-  (mapcar (lambda (x) (string-trim " " x)) (split-sequence:split-sequence #\, str)))
197
+  (mapcar (lambda (x) (string-trim " " x))
198
+          (split-sequence:split-sequence #\, str)))
199
+
200
+(defun get-set-id (blog-seed set-name author-name)
201
+  (let* ((o nil)
202
+         (digester (ironclad:make-digest :tiger))
203
+         (digest (progn
204
+                   (ironclad:update-digest digester blog-seed)
205
+                   (ironclad:update-digest digester (ironclad:ascii-string-to-byte-array set-name))
206
+                   (ironclad:update-digest digester (ironclad:ascii-string-to-byte-array author-name))
207
+                   (ironclad:byte-array-to-hex-string
208
+                     (ironclad:produce-digest digester))))
209
+         (digest-length (length digest)))
210
+    (dotimes (x (ceiling (/ digest-length 5)))
211
+      (push (subseq digest
212
+                    (* x 5)
213
+                    (min digest-length (* (1+ x) 5)))
214
+            o))
215
+    (format nil "~{~a~^-~}" o)))
92 216
 
93 217
 (setf (route *app* "/post/new" :method :POST)
94 218
       (lambda (params)
95 219
         (format nil "~s" params)
96
-        (let ((new-post
220
+        (let* ((author (str-assoc-cdr "author" params))
221
+               (set (str-assoc-cdr "set" params))
222
+               (new-post
97 223
                 (make-post *blog*
98 224
                            (str-assoc-cdr "title" params)
99 225
                            (str-assoc-cdr "post" params)
100
-                           (str-assoc-cdr "author" params)
101
-                           (str-assoc-cdr "set" params)
226
+                           author
227
+                           set
102 228
                            :tags (split-tags (str-assoc-cdr "tags" params)))))
103 229
           (with-page (:title (post-title new-post))
104 230
             (:header
105
-              (:h1 (post-title new-post)))
106
-            (:section :class "post-show"
107
-             (:article
108
-               (post-text new-post))
109
-             (:span :class "author"
110
-              (post-author new-post))
111
-             (:span :class "set"
112
-              (post-set new-post)))))))
231
+              (:h1 "What a Piece of Blog is This?"))
232
+            (format-post new-post)))))
113 233
 
114 234
 (setf (route *app* "/" :method :GET)
115 235
       (lambda (params)
116
-        (with-page (:title "Blog")
236
+        (declare (ignore params))
237
+        (with-page (:title "What a Piece of Blog is This?")
117 238
           (:header
118
-            (:h1 "Blog"))
239
+            (:h1 "What a Piece of Blog is This?"))
119 240
           (loop for new-post in (blog-posts *blog*)
120 241
                 collect
121
-                (:section :class "post-show"
122
-                 (:h2
123
-                   (post-title new-post))
124
-                 (:article
125
-                   (post-text new-post))
126
-                 (:span :class "author"
127
-                  (post-author new-post))
128
-                 (:span :class "set"
129
-                  (post-set new-post)))))))
242
+                (format-post new-post)))))
130 243
 
131 244
 
132 245
 (defparameter *handler*
... ...
@@ -136,4 +249,4 @@
136 249
       ;:csrf
137 250
       ;clack-errors:*clack-error-middleware*
138 251
       (:static :path "/static/" :root #p"./static/")
139
-      *app*) :port 5000))
252
+      *app*) :port 5050))
... ...
@@ -5,29 +5,77 @@
5 5
 }
6 6
 
7 7
 body {
8
-  background:  hsl(44, 87%, 94%);
8
+  background: #fdf6e3;
9 9
   color: hsl(194, 14%, 40%);
10
-  text-rendering: geometric-precision;
10
+  text-rendering: geometricPrecision;
11 11
   font-family: Caudex;
12
+  padding-top: 10vh;
13
+}
14
+
15
+main {
16
+  background: #eee8d5;
17
+  width: 66vw;
18
+  margin-left: 16.5vw;
19
+  display:block;
12 20
 }
13 21
 
14 22
 header {
15
-  padding: 2em;
23
+  /*padding: 2em;*/
16 24
   font-size: 2vmin;
17 25
   height: 10vh;
18 26
   width: 66vw;
19
-  margin-left: 16.5vw;
20 27
   border: thin solid black;
28
+  position: fixed;
29
+  top: 0px;
30
+  line-height: 10vh;
31
+  text-align: center;
32
+  z-index: 100;
33
+}
34
+
35
+header h1 {
36
+  text-shadow: 0px 0px 0.5em hsl(194, 14%, 40%);
37
+  color:  hsl(44, 87%, 94%);
38
+  padding: 0px;
39
+  margin: 0px;
40
+}
41
+
42
+h1, h2 {
43
+  text-align: center;
44
+  margin-top: 1em;
45
+  margin-bottom: 0.5em;
46
+}
47
+
48
+
49
+
50
+header::before {
51
+  position: absolute;
52
+  top: 0px;
53
+  bottom: 0px;
54
+  left: 0px;
55
+  right: 0px;
56
+  content: " ";
57
+  display: block;
58
+  background:  hsla(194, 14%, 40%, 0.7);
59
+  z-index: -1;
60
+  -webkit-backdrop-filter: blur(2px);
21 61
 }
22 62
 
23 63
 section.post-new, section.post-show {
24 64
   padding: 2em;
25
-  width: 66vw;
26
-  margin-left: 16.5vw;
27
-  border: thin solid black;
65
+  overflow: hidden;
66
+  border: thin solid hsl(194, 14%, 40%);
67
+  border-top: none;
68
+  border-bottom-width: medium;
69
+  border-bottom-style: double;
28 70
 }
71
+
29 72
 section.post-new {
30
-  height: 90vh;
73
+  overflow-y: auto;
74
+  min-height: 90vh;
75
+}
76
+
77
+form {
78
+  position: relative;
31 79
 }
32 80
 
33 81
 input[type=text], textarea {
... ...
@@ -36,11 +84,39 @@ input[type=text], textarea {
36 84
 
37 85
 input, textarea {
38 86
   padding: 0.5em 1em;
39
-  border: thin solid black;
40
-  background:   hsl(44, 87%, 94%);
87
+  border: thin solid #fdf6e3;
88
+  border-radius: 0.5rem;
89
+  background:  #fdf6e3;
41 90
   color: hsl(194, 14%, 40%);
42 91
 }
43 92
 
93
+input[type=submit], button {
94
+  color: hsl(44, 87%, 94%);
95
+  text-shadow: 0px 0px 0.2em hsl(194, 14%, 40%), 0px 0px 0.2em hsl(44, 87%, 94%);
96
+  background-image: linear-gradient(
97
+    hsla(194, 14%, 40%, 0.25),
98
+    hsla(194, 14%, 40%, 0.5),
99
+    hsl(194, 14%, 40%)
100
+  );
101
+}
102
+
103
+div.input-group {
104
+  display: inline-block;
105
+}
106
+
107
+input[type=submit]:hover, button:hover {
108
+  background-image: linear-gradient(
109
+    hsl(194, 14%, 40%),
110
+    hsla(194, 14%, 40%, 0.5),
111
+    hsla(194, 14%, 40%, 0.25)
112
+  );
113
+}
114
+
115
+input[type=text], textarea {
116
+  box-shadow: inset 0em 0em 1em #eee8d5,
117
+              0em 0em 1em #eee8d5;
118
+}
119
+
44 120
 section.post-new textarea {
45 121
   display: block;
46 122
   width: 100%;
... ...
@@ -48,29 +124,63 @@ section.post-new textarea {
48 124
   margin-bottom: 1em;
49 125
 }
50 126
 
51
-input[name=title] {
127
+.input-group.title {
52 128
   width: 100%;
129
+}
130
+
131
+.input-group.title label,
132
+.input-group.title input {
53 133
   font-size: 150%;
54 134
   font-weight: bolder;
55 135
   margin-bottom: 1em;
56 136
 }
57
-input[name=author] {
137
+
138
+.input-group.title label, .input-group.author label {
139
+  width: 33%;
140
+  text-align: right;
141
+  display: inline-block;
142
+}
143
+
144
+.input-group label {
145
+  text-transform: uppercase;
146
+}
147
+
148
+.input-group.post {
149
+  width: 100%;
150
+}
151
+
152
+.input-group.title input, .input-group.author input {
153
+  display: inline-block;
58 154
   width: 66%;
59
-  margin-left: 34%;
155
+}
156
+
157
+.input-group.author {
158
+  width: 100%;
60 159
   margin-bottom: 1em;
61 160
 }
62 161
 
63
-input[name=set] {
162
+.input-group.set {
64 163
   width: 24%;
65 164
   margin-bottom: 1em;
66 165
 }
67 166
 
68
-input[name=tags] {
167
+input[type=text] {
168
+  width: 100%;
169
+}
170
+
171
+.input-group.tags {
69 172
   width: 74%;
70 173
   margin-left: 1%;
71 174
   margin-bottom: 1em;
72 175
   clear: both;
73 176
 }
74 177
 
75
-input[type=submit] {
178
+input[type=submit], button {
179
+  cursor: pointer;
180
+  position: absolute;
181
+  right: 0px;
182
+}
183
+
184
+ul, ol {
185
+  margin-left: 1.5em;
76 186
 }