git.fiddlerwoaroof.com
Browse code

initial commit

fiddlerwoaroof authored on 26/06/2017 16:03:00
Showing 6 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1 @@
1
+*.fasl
0 2
new file mode 100644
... ...
@@ -0,0 +1,25 @@
1
+;;;; package.lisp
2
+
3
+(in-package :fwoar.sp-user)
4
+
5
+(defmacro define-package (name &body arguments)
6
+  (let ((uses (cons :use
7
+		    (union '(:cl :alexandria :serapeum)
8
+			   (cdr (assoc :use arguments))))))
9
+    `(eval-when (:compile-toplevel :load-toplevel :execute)
10
+       (defpackage ,name
11
+	 ,uses
12
+	 ,@(remove :use arguments :key 'car)))))
13
+
14
+(define-package :stream-provider
15
+  (:use :fw.lu)
16
+  (:export
17
+   #:get-stream-for
18
+   #:stream-provider
19
+   #:string-provider
20
+   #:file-provider
21
+   #:stream-key
22
+   #:root
23
+   #:streams
24
+   #:with-storage-stream))
25
+
0 26
new file mode 100644
... ...
@@ -0,0 +1,35 @@
1
+;;;; stream-provider.asd
2
+
3
+(cl:defpackage :fwoar.sp-system
4
+  (:use :cl :asdf))
5
+(in-package :fwoar.sp-system)
6
+
7
+(asdf:defsystem #:stream-provider
8
+  :description "A class library for interacting with groups of related resources"
9
+  :author "Edward Langley <el-streamprovider@elangley.org"
10
+  :license "MIT"
11
+  :in-order-to ((test-op (test-op "stream-provider.test")))
12
+  :depends-on (#:fwoar.lisputils
13
+               #:alexandria
14
+               #:serapeum
15
+	       #:uiop
16
+	       #:flexi-streams
17
+	       #:vector-update-stream)
18
+  :serial t
19
+  :components ((:file "package")
20
+               (:file "stream-provider")))
21
+
22
+(asdf:defsystem #:stream-provider.test
23
+  :depends-on (#:stream-provider
24
+	       #:should-test)
25
+  :serial t
26
+  :components ((:file "package")
27
+	       (:file "tests"))
28
+  :perform (asdf:test-op (o s)
29
+		    (let ((*package* (find-package :stream-provider.tests)))
30
+		      (uiop:symbol-call :should-test
31
+					:test))))
32
+
33
+(defpackage :fwoar.sp-user
34
+  (:use cl)
35
+  (:export #:define-package))
0 36
new file mode 100644
1 37
Binary files /dev/null and b/stream-provider.fasl differ
2 38
new file mode 100644
... ...
@@ -0,0 +1,70 @@
1
+(fwoar.sp-user:define-package :stream-provider
2
+  (:use :fw.lu)
3
+  (:export #:get-stream-for
4
+	   #:stream-provider
5
+	   #:string-provider
6
+	   #:file-provider
7
+	   #:stream-key
8
+	   #:root
9
+	   #:streams
10
+	   #:with-storage-stream))
11
+(cl:in-package :stream-provider)
12
+
13
+(defclass stream-provider ()
14
+  ())
15
+
16
+(defvar *store-root* *default-pathname-defaults*)
17
+
18
+(defgeneric stream-key (provider item)
19
+  (:documentation "return a key for an item: must be able to be compared with EQUAL
20
+
21
+If you override on provider, make sure to CALL-NEXT-METHOD"))
22
+
23
+(defgeneric get-stream-for (provider streamable &rest extra-args)
24
+  (:documentation "get a stream for a given streamable object"))
25
+
26
+(defgeneric root (provider)
27
+  (:documentation "get the base path for the streams"))
28
+
29
+
30
+(defclass string-provider (stream-provider)
31
+  ((%streams :reader streams :initform (make-hash-table :test 'equal))))
32
+
33
+(defmethod root ((provider string-provider))
34
+  #p "/")
35
+
36
+(defmethod stream-key :around (provider item)
37
+  (let ((key (call-next-method)))
38
+    (check-type key (or string pathname))
39
+    (uiop:enough-pathname key (root provider))))
40
+
41
+(defmethod get-stream-for ((provider string-provider) streamable &rest extra-args)
42
+  (declare (ignore extra-args))
43
+  (with-accessors* (streams) provider
44
+    (vector-update-stream:make-update-stream 
45
+     (setf (gethash (stream-key provider streamable) streams)
46
+	   (make-array 10
47
+		       :element-type 'octet
48
+		       :adjustable t
49
+		       :fill-pointer 0)))))
50
+
51
+(defclass file-provider (stream-provider)
52
+  ((%root :initarg :root :initform (error "need a root for a file-provider") :reader root)
53
+   (%if-exists :initarg :if-exists :initform :supersede :reader if-exists)))
54
+
55
+(defmethod get-stream-for ((provider file-provider) streamable &rest extra-args)
56
+  (declare (ignore extra-args))
57
+  (with-accessors* (if-exists root) provider
58
+    (let ((stream-key (merge-pathnames (stream-key provider streamable)
59
+				       root)))
60
+      (when (eql if-exists :if-exists)
61
+	(ensure-directories-exist stream-key))
62
+      (open stream-key :direction :output :if-exists if-exists
63
+	    :element-type 'octet))))
64
+
65
+(defmacro with-storage-stream ((stream-sym object provider &rest extra-args) &body body)
66
+  (once-only (object)
67
+    `(let ((,stream-sym (flexi-streams:make-flexi-stream (get-stream-for ,provider ,object ,@extra-args)
68
+							 :external-format :utf-8)))
69
+       (unwind-protect (progn ,@body)
70
+	 (close ,stream-sym)))))
0 71
new file mode 100644
... ...
@@ -0,0 +1,56 @@
1
+(fwoar.sp-user:define-package :stream-provider.tests
2
+  (:use :cl :stream-provider :should-test))
3
+(in-package :stream-provider.tests)
4
+
5
+(defparameter *test-string-provider* (make-instance 'string-provider))
6
+(defparameter *test-file-provider* (make-instance 'file-provider :root #p"/nowhere/"))
7
+
8
+(defmethod stream-key ((provider (eql *test-string-provider*)) (item string))
9
+  item)
10
+
11
+(defmethod stream-key ((provider (eql *test-file-provider*)) (item string))
12
+  item)
13
+
14
+(deftest stream-key-returns-specified-pathname ()
15
+    (let* ((stored-item "foo")
16
+	   (expected-pathname (pathname "foo")))
17
+      (should be equal
18
+	      (stream-key *test-string-provider* stored-item)
19
+	      expected-pathname)))
20
+
21
+
22
+
23
+(deftest stream-key-for-file-provider-is-relative-to-root ()
24
+    (let* ((stored-item "/nowhere/foo")
25
+	   (expected-pathname (pathname "foo")))
26
+      (should be equal
27
+	      expected-pathname
28
+	      (stream-key *test-file-provider* stored-item))))
29
+
30
+(defclass object-to-store ()
31
+  ((%name :reader name :initarg :name :initform (error "need a name"))
32
+   (%value :reader value :initarg :value :initform (error "need a value"))))
33
+
34
+(defclass test-provider (string-provider)
35
+  ())
36
+
37
+(defmethod stream-key ((provider test-provider) (item object-to-store))
38
+  (name item))
39
+
40
+(defun store (provider item)
41
+  (with-storage-stream (s item provider)
42
+    (write-sequence (value item) s)))
43
+
44
+(deftest with-storage-stream-writes-to-right-place ()
45
+    (let* ((name "foo")
46
+	   (value "bar bar")
47
+	   (object (make-instance 'object-to-store
48
+				  :name name
49
+				  :value value))
50
+	   (provider (make-instance 'test-provider)))
51
+      (should be equal
52
+	      (store provider object)
53
+	      value
54
+	      (babel:octets-to-string
55
+	       (gethash (stream-key provider object)
56
+			(streams provider))))))