From peddy at common-lisp.net Sat Dec 1 14:19:59 2007 From: peddy at common-lisp.net (peddy) Date: Sat, 1 Dec 2007 09:19:59 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb Message-ID: <20071201141959.EA9654D046@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb In directory clnet:/tmp/cvs-serv26692 Log Message: Import Status: Vendor Tag: peter Release Tags: start N clouchdb/public_html/index.html N clouchdb/public_html/style.css N clouchdb/src/README.txt N clouchdb/src/changelog.txt N clouchdb/src/clouchdb-examples.asd N clouchdb/src/clouchdb-tests.asd N clouchdb/src/clouchdb.asd N clouchdb/src/clouchdb.lisp N clouchdb/src/examples.lisp N clouchdb/src/package.lisp N clouchdb/src/tests.lisp No conflicts created by this import From peddy at common-lisp.net Sat Dec 8 15:16:21 2007 From: peddy at common-lisp.net (peddy) Date: Sat, 8 Dec 2007 10:16:21 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071208151621.9D8927A00B@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv31642/src Modified Files: clouchdb.lisp Log Message: Fixed/cleaned up db conditions --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/01 14:19:59 1.1.1.1 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/08 15:16:21 1.2 @@ -42,15 +42,26 @@ ;; (define-condition db-existential-error (error) - ((text :initarg :text :reader text) + ((text :initarg :uri :reader uri) (db-name :initarg :db-name :reader db-name) (result :initarg :result :reader result))) -(define-condition db-does-not-exist (db-existential-error) ()) -(define-condition db-already-exists (db-existential-error) ()) +(define-condition db-does-not-exist (db-existential-error) + () + (:report (lambda (condition stream) + (format stream "Database \"~A\" at \"~A\" does not exist" + (db-name condition) + (uri condition))))) + +(define-condition db-already-exists (db-existential-error) + () + (:report (lambda (condition stream) + (format stream "Database \"~A\" at \"~A\" already exists" + (db-name condition) + (uri condition))))) (define-condition doc-error (error) - ((text :initarg :text :reader text) + ((text :initarg :uri :reader text) (reason :initarg :reason :reader reason) (id :initarg :id :reader id))) @@ -62,8 +73,7 @@ (define-condition id-missing (doc-error) () - (:report (lambda (condition stream) - (format stream "No ID specified")))) + (:report (lambda (condition stream) (format stream "No ID specified")))) (define-condition document-missing (doc-error) () @@ -132,9 +142,8 @@ (let ((dbn (if ,db-name-p ,db-name *db-name*))) (if (document-property :error (get-db-info :db-name dbn)) (error 'db-does-not-exist - :result ,result :dbn dbn - :text (format t "Database ~S does not exist" - (make-uri dbn)))))) + :result ,result :db-name dbn + :uri (make-uri dbn))))) ,result))) ;; @@ -247,8 +256,7 @@ (restart-case (error 'db-already-exists :result res :db-name name - :text (format t "Database ~S already exists" - (make-uri name))) + :uri (make-uri name)) (ignore () :report "Ignore error and continue" nil)))) res))) @@ -263,8 +271,7 @@ (restart-case (error 'db-does-not-exist :result res :db-name name - :text (format t "Database ~S does not exist" - (make-uri name))) + :uri (make-uri name)) (ignore () :report "Ignore error and continue" nil))) res)) From peddy at common-lisp.net Sun Dec 9 16:03:21 2007 From: peddy at common-lisp.net (peddy) Date: Sun, 9 Dec 2007 11:03:21 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/public_html Message-ID: <20071209160321.73E892105F@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/public_html In directory clnet:/tmp/cvs-serv2019/public_html Modified Files: index.html Log Message: - Fixed ad-hoc-view and invoke-view functions so that they now use all CouchDb view options - Added generic keyword parameter to URL parameter translation code - Changed default CouchDb port to IANA assigned 5984 - Added more comments --- /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/01 14:19:59 1.1.1.1 +++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/09 16:03:21 1.2 @@ -32,6 +32,7 @@

Contents

+

News

+

+

+

+

Download and Installation

The current download link for clouchdb can be found at @@ -135,10 +155,10 @@

NB: If you try these examples I suggest also viewing the results via CouchDb's HTML UI - at - http://localhost:8888/_utils/browse/index.html, of course you'll - need to adjust the URL for the actual CouchDb server and port in - use. + at http://localhost:8888/_utils/browse/index.html, + (or http://localhost:5984/_utils/browse/index.html + if you're using ClouchDb version 7.2 or later), of course you'll need to + adjust the URL for the actual CouchDb server and port in use.

Example 1

@@ -164,7 +184,7 @@ ;; Get CouchDb Server Information by specifying a nil DB name CLOUCHDB-USER> (get-db-info :db-name nil) ((:COUCHDB . "Welcome") (:VERSION . "0.7.0a575")) -;; Create the database +;; Create database "test-db", which we named above CLOUCHDB-USER> (create-db) ((:OK . T)) ;; Create a document with one field, and give it an ID of "gjc" @@ -345,6 +365,22 @@ error if the database already exists. A value of :ignore will simply ignore the this error. A value of :recreate will delete the database if it exists, and then recreate it. +

+Example: +

+;; Create the database named in the current connection settings
+(set-connection :db-name "tvland")
+(create-db)
+=> ((:OK . T))
+
+;; Specify name of database to create, if it already exists
+;; then ignore the request (don't generate error, don't
+;; recreate database), return (:ignored . T) if database did
+;; exist
+(create-db :db-name "tvland"
+=> ((:OK . T) (:IGNORED . T))
+
+

[Function]
@@ -357,6 +393,25 @@

If :ignore is specified for the if-missing parameter, errors resulting from the attempt to delete a non-existent database are ignored. + +

+Example: +

+;; Create the database named in the current connection settings
+(set-connection :db-name "tvland")
+(delete-db)
+=> ((:OK . T))
+
+;; Specify name of database to delete, if it doesn't exist
+;; then ignore the request (don't generate error), return 
+;; ((:ERROR . "not_found") (:REASON . "missing")) if 
+;; database did not exist
+(delete-db :db-name "tvland"
+=> ((:ERROR . "not_found") (:REASON . "missing"))
+
+

+ +

[Function]
@@ -387,7 +442,7 @@ Sets the host name, database name, protocol ("http" or "https") and port number for the top-level connection to the CouchDb server. Default connection settings are host="localhost", -protocol="http", port="8888" and database="default". +protocol="http", port="5984" and database="default".

See (with-connection) @@ -406,6 +461,13 @@ ;; Get document from specified host and database (with-connection (:host "cornichon.cucumber.net" :db-name "rfc") (get-document "2616")) + +;; Copy document identified by "someid" from database "otherdb" to +;; current database, use "copy-of-someid" for copied document ID. +(put-document + (with-connection (:db-name "otherdb") + (get-document "someid")) + :id "copy-of-someid")

See (set-connection) @@ -582,13 +644,56 @@ Please refer to CouchDb View API Documentation for general information about CouchDb views. + Note: Many details of CouchDb views have yet to be + documented. In the meantime, + see this + blog post for some useful hints.

[Function]
-ad-hoc-view view +ad-hoc-view view &key key + start-key start-key-docid end-key count update + descending skip

Executes a one-time, non persistent view (query). The view is specified as a JavaScript anonymous function. +

+ Keyword parameters +

+

Example:

@@ -662,9 +767,49 @@
 

[Function]
-invoke-view id view :key key +invoke-view id view &key key + start-key start-key-docid end-key count update descending skip +

Invoke specified view in identified view document. +

+ Keyword parameters +

+

+

Example:




From peddy at common-lisp.net  Sun Dec  9 16:03:21 2007
From: peddy at common-lisp.net (peddy)
Date: Sun,  9 Dec 2007 11:03:21 -0500 (EST)
Subject: [clouchdb-cvs] CVS clouchdb/src
Message-ID: <20071209160321.BE4D328067@common-lisp.net>

Update of /project/clouchdb/cvsroot/clouchdb/src
In directory clnet:/tmp/cvs-serv2019/src

Modified Files:
	changelog.txt clouchdb.lisp examples.lisp tests.lisp 
Log Message:

  - Fixed ad-hoc-view and invoke-view functions so that they now use
    all CouchDb view options
  - Added generic keyword parameter to URL parameter translation code
  - Changed default CouchDb port to IANA assigned 5984 
  - Added more comments



--- /project/clouchdb/cvsroot/clouchdb/src/changelog.txt	2007/12/01 14:19:59	1.1.1.1
+++ /project/clouchdb/cvsroot/clouchdb/src/changelog.txt	2007/12/09 16:03:21	1.2
@@ -1,4 +1,12 @@
 
+0.0.4:
+
+  - Fixed ad-hoc-view and invoke-view functions so that they now use
+    all CouchDb view options
+  - Added generic keyword parameter to URL parameter translation code
+  - Changed default CouchDb port to IANA assigned 5984 
+  - Added more comments
+
 0.0.3:
 
   - Changed 'scheme' parameter of set-connection to 'protocol'
--- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp	2007/12/08 15:16:21	1.2
+++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp	2007/12/09 16:03:21	1.3
@@ -25,7 +25,7 @@
 (in-package :clouchdb)
 
 (defvar *host* "localhost" "CouchDb server host name")
-(defvar *port* "8888" "CouchDb port")
+(defvar *port* "5984" "As of version 7.2, the IANA assigned CouchDb port (was 8888)")
 (defvar *db-name* "default" "Default database name")
 (defvar *protocol* "http" "http or https")
 
@@ -38,6 +38,47 @@
 (defparameter *temp-db-counter* 0 "Used in the creation of temporary databases")
 
 ;;
+;; URL Parameter helpers
+;;
+
+(defun true-if-true (value)
+  "Return \"true\" if value is non-nil, otherwise nil"
+  (when value "true"))
+
+(defun false-if-false (value)
+  "Return \"false\" if value is nil, otherwise nil"
+  (unless value "false"))
+
+(defvar *view-options*
+  '((:key . ((:name . "key") (:fn . doublequote)))
+    (:start-key . ((:name . "startkey") (:fn . doublequote)))
+    (:start-key-docid . ((:name . "startkey_docid") (:fn . doublequote)))
+    (:end-key . ((:name . "endkey") (:fn . doublequote)))
+    (:count . ((:name . "count") (:fn . value-as-string)))
+    (:update . ((:name . "update") (:fn . false-if-false)))
+    (:descending . ((:name . "descending") (:fn . true-if-true)))
+    (:skip . ((:name . "skip") (:fn . value-as-integer))))
+  "Definitions for how invoke-view keyword parameters are translated
+  into CouchDb parameters")
+
+(defun transform-param (param value table)
+  "Use a keyword transformation table to traslate between function
+  keyword parameter names and values, and URL parameter names and
+  values."
+  (let ((transf (cdr (assoc param table))))
+    (when transf
+      (let ((value (funcall (cdr (assoc :fn transf)) value)))
+        (when value
+          (cons (cdr (assoc :name transf)) value))))))
+
+(defun transform-params (keyword-params options)
+  "Transform each keyword parameter using the specified set of
+options, use only those transformations that return a non-nil result."
+  (loop for param on keyword-params by #'cddr
+     when (transform-param (first param) (second param) options)
+     collect it))
+
+;;
 ;; Conditions
 ;;
 
@@ -92,6 +133,12 @@
 	((stringp value)
 	 value)))
 
+(defun value-as-integer (value)
+  (cond ((numberp value)
+         value)
+        ((stringp value)
+         (parse-integer value))))
+
 (defun string-join (list &optional (delim #\,)) 
   "Join a bunch of strings into one with a delimiter. Implementation
 by Russel McManus on c.l.l."
@@ -123,15 +170,6 @@
   convert :db-name to *db-name*"
   (intern (cat "*" (string-upcase (symbol-name key)) "*")))
 
-(defun make-lets (args)
-  "Convert a list of keyword/value pairs to a form suitable for the
-innerds of a let. For example, convert '(:db-name (fn1 x x) :host
-'host') to ((*db-name* (fn1 x x) (*host* 'host'))"
-  (if (null args)
-      nil
-      (cons (list (keyword-to-special (first args)) (second args))
-	    (make-lets (cddr args)))))
-
 (defmacro ensure-db ((&key (db-name nil db-name-p)) &body body)
   "Warp request in code to check for errors due to non-existant data
 bases. Since in a document operation, CouchDb does not distinguish
@@ -192,10 +230,12 @@
 
 (defun db-request (uri &rest keys &key &allow-other-keys)
   "Used by all Couchdb APIs to make the actual REST request."
-  ;;(format t "uri: ~S~% keys: ~S~%" (make-uri uri) keys)
+;;  (format t "uri: ~S~% keys: ~S~%" (make-uri uri) keys)
   (let ((*text-content-types* *text-types*))
     (multiple-value-bind (body status headers uri stream must-close reason-phrase)
 	(apply #'drakma:http-request (make-uri uri) keys)
+;;      (format t "  -> uri: ~S~%" uri)
+;;      (format t "  -> headers: ~S~%" headers)
       (if must-close 
 	  (json:decode-json-from-string body)
 	  nil))))
@@ -206,7 +246,9 @@
 
 (defun set-connection (&key (host nil host-p) (db-name nil db-name-p) 
 		       (protocol nil protocol-p) (port nil port-p))
-  "Set top-level connection information."
+  "Set top-level connection information. The port may be specified as
+a string or number. As of CouchDb version 7.2 the default port is
+5984, prior to that it was 8888."
   (when host-p (setf *host* host))
   (when db-name-p (setf *db-name* db-name))
   (when port-p (setf *port* (value-as-string port)))
@@ -216,9 +258,12 @@
 (defmacro with-connection ((&rest args &key db-name port protocol host)
 			   &body body)
   "Execute body in the context of the optionally specified host,
-db-name, port or protocol."
+db-name, port or protocol. Port may be a string or a number, protocol
+is http or https. As of CouchDb version 7.2 the default port is 5984,
+prior to that it was 8888."
   (declare (ignore db-name port protocol host))
-  `(let (,@(make-lets args))
+  `(let (,@(loop for var on args 
+              by #'cddr collect (list (keyword-to-special (car var)) (second var))))
      , at body))
 
 (defun document-properties (document)
@@ -362,8 +407,9 @@
 	  ;; If an ID was specified and that ID does not match the
 	  ;; :_id property of the current document, strip the document
 	  ;; of all special (CouchDb managed) properties, since these
-	  ;; are specific to the current document. This will create a
-	  ;; new document with the same contents as the old one.
+	  ;; are specific to the current document. In this case, the
+	  ;; presence of the ID parameter means 'create a new document
+	  ;; with the same contents as the old one'.
 	  ((and id current-id (not (equal current-id id)))
 	   (setf doc (document-properties doc))))
     (let ((res (ensure-db ()
@@ -426,12 +472,14 @@
 ;; Views API
 ;;
 
-(defun ad-hoc-view (view)
+(defun ad-hoc-view (view &rest options &key key start-key start-key-docid
+                    end-key count update descending skip)
   "Execute query using an ad-hoc view."
   (ensure-db ()
     (db-request (cat *db-name* "/_temp_view")
 		:method :post
 		:content-type "text/javascript"
+                :parameters (transform-params options *view-options*)
 		:content view)))
 
 (defun create-view (id &rest view-defs)
@@ -456,10 +504,18 @@
   (ensure-db ()
     (delete-document :id (cat "_design/" id) :revision revision)))
 
-(defun invoke-view (id view &key (key nil))
+(defun invoke-view (id view &rest options &key key start-key start-key-docid
+                    end-key count update descending skip)
   "Invoke a view by specifiying the document ID that contains the view
-and the name of the contained view."
+and the name of the contained view. The key parameter specifies an
+optional value to match against the view's mapped field. The start-key
+and end-key values specify the optional begin and end range of the
+mapped field(s) of each document to return. If descending is t,
+returns results in reverse order. If update is t, does not refresh
+view for query, use for higher performance but possible data
+inconsistency."
+  (declare (ignore key start-key start-key-docid end-key count update descending skip))
   (ensure-db ()
     (db-request (cat *db-name* "/_view/" id "/" view)
 		:method :get
-		:parameters (if key `(("key" . ,(doublequote key)))))))
+                :parameters (transform-params options *view-options*))))
--- /project/clouchdb/cvsroot/clouchdb/src/examples.lisp	2007/12/01 14:19:59	1.1.1.1
+++ /project/clouchdb/cvsroot/clouchdb/src/examples.lisp	2007/12/09 16:03:21	1.2
@@ -33,7 +33,11 @@
 ;; represent the defaults.
 
 ;(defparameter *host* "localhost")
+
+;; Port for CouchDb versions prior to 7.2
 ;(defparameter *port* "8888")
+;; Port CouchDb 7.2 and later
+;(defparameter *port* "5984") 
 ;(defparameter *scheme* "http")
 
 ;;
@@ -52,7 +56,7 @@
     ;; Create a document with ID of "haussmann"
     (create-document '(;; Fields with scalar values:
 		       (:name . "Georges-Eugene Haussmann") 
-		       ;; Note: above should be "Georges-Eug??ne", but
+		       ;; Note: above should be "Georges-Eug?ne" but
 		       ;; there's a character handling bug somewhere
 		       (:aka . "Baron Haussmann")
 		       (:born . "1809-03-27")
--- /project/clouchdb/cvsroot/clouchdb/src/tests.lisp	2007/12/01 14:19:59	1.1.1.1
+++ /project/clouchdb/cvsroot/clouchdb/src/tests.lisp	2007/12/09 16:03:21	1.2
@@ -111,7 +111,7 @@
   (:dynamic-variables 
    (*db-name* nil) 
    (*host* "localhost") 
-   (*port* "8888") 
+   (*port* "5984") 
    (*protocol* "http")))
 
 ;;
@@ -233,6 +233,11 @@
   (ensure-condition 'id-missing (put-document '((:a "test")))))
 
 (addtest (clouchdb-doc-api-tests)
+  (:documentation "Get a non-existant document (error)")
+  get-non-existant-document
+  (ensure-condition 'document-missing (get-document "does-not-exist")))
+
+(addtest (clouchdb-doc-api-tests)
   (:documentation "Test revision info")
   get-document-revision-info
   (ensure-same 11 (progn 



From peddy at common-lisp.net  Fri Dec 14 23:22:58 2007
From: peddy at common-lisp.net (peddy)
Date: Fri, 14 Dec 2007 18:22:58 -0500 (EST)
Subject: [clouchdb-cvs] CVS clouchdb/public_html
Message-ID: <20071214232258.8D58A4F00E@common-lisp.net>

Update of /project/clouchdb/cvsroot/clouchdb/public_html
In directory clnet:/tmp/cvs-serv19826/public_html

Modified Files:
	index.html 
Log Message:
Fixed document ID encoding bug, documentation now valid XHMTL

--- /project/clouchdb/cvsroot/clouchdb/public_html/index.html	2007/12/09 16:03:21	1.2
+++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html	2007/12/14 23:22:58	1.3
@@ -41,23 +41,22 @@
 
 
 

News

-

-

-

+ +

Download and Installation

@@ -68,15 +67,14 @@

Requirements:

-

-

-

+

ASDF Install

@@ -101,13 +99,14 @@

The clouchdb distribution comes with a unit test suite which uses the LIFT testing framework. To run the tests, follow the following steps: +

 (asdf:oos 'asdf:load-op '#:clouchdb-tests)
 (in-package :clouchdb-tests)
 (run-all-tests)
 
-

+

Note that if the CouchDb server is not running on the same host you will have to modify tests.lisp to point it to the appropriate host. @@ -127,7 +126,18 @@

Be sure to look at examples.lisp to understand what each example is doing. - +

+ +

CVS Access

+ +

+ Clouchdb project hosting is graciously provided by common-lisp.net, + the CVS repository may be checked out anonymously as follows: +

+ +
+cvs -z3 -d :pserver:anonymous:anonymous at common-lisp.net:/project/clouchdb/cvsroot co clouchdb
+

Support and mailing lists

@@ -135,15 +145,17 @@ The following email lists have been provided by the common-lisp.net for clouchdb development and information: -

+

Examples

@@ -162,13 +174,13 @@

Example 1

-The following example session demonstrates: +

The following example session demonstrates:

@@ -203,14 +215,14 @@
 

Example 2

-Demonstrating: +

Demonstrating:

@@ -277,14 +289,14 @@
 

Example 3

-Demonstrating: +

Demonstrating:

@@ -311,7 +323,7 @@
 
 ;; Create a persistent view document to find cities in the
 ;; Netherlands and also to find cities by country key. 
-;; Note: Expressions within the (ps) expressions are Parenscript,
+;; Note: Expressions within the (ps) expressions are Parenscript,
 ;; a lispy way to generate JavaScript.
 CLOUCHDB-USER> (create-view "cities"
                             (cons "country"
@@ -347,26 +359,34 @@
 
-

API Reference

API Reference

Server Connection and Database Management API

-The functions described in this section are related to the +

+ The API described in this has to do with managing CouchDb server + information and the creation and deletion of databases. +

-

[Function]
-create-db &key db-name if-exists +

[Function]
+create-db &key db-name if-exists +

-Create a database. The db-name can be specified, otherwise attempts to -create the database named in the current context (either through -(set-connection) or -(with-connection). - -

The if-exists parameter defaults to :fail, which will raise an -error if the database already exists. A value of :ignore will simply -ignore the this error. A value of :recreate will delete the database -if it exists, and then recreate it. +

+ Create a database. The db-name can be specified, otherwise attempts + to create the database named in the current context (either through + (set-connection) or + (with-connection). +

+

+ The if-exists parameter defaults to :fail, which will raise an error + if the database already exists. A value of :ignore will simply + ignore the this error. A value of :recreate will delete the database + if it exists, and then recreate it. +

Example: +

 ;; Create the database named in the current connection settings
 (set-connection :db-name "tvland")
@@ -377,25 +397,29 @@
 ;; then ignore the request (don't generate error, don't
 ;; recreate database), return (:ignored . T) if database did
 ;; exist
-(create-db :db-name "tvland"
+(create-db :db-name "tvland")
 => ((:OK . T) (:IGNORED . T))
 
-

+

-

[Function]
-delete-db &key db-name if-missing +

[Function]
+delete-db &key db-name if-missing +

+

Delete a database. The db-name can be specified, otherwise attempts to delete the database named in the current context (either through (set-connection) or -(with-connection). - -

If :ignore is specified for the if-missing parameter, errors -resulting from the attempt to delete a non-existent database are ignored. - +(with-connection). +

+

+If :ignore is specified for the if-missing parameter, errors resulting +from the attempt to delete a non-existent database are ignored. +

Example: +

 ;; Create the database named in the current connection settings
 (set-connection :db-name "tvland")
@@ -406,57 +430,66 @@
 ;; then ignore the request (don't generate error), return 
 ;; ((:ERROR . "not_found") (:REASON . "missing")) if 
 ;; database did not exist
-(delete-db :db-name "tvland"
+(delete-db :db-name "tvland")
 => ((:ERROR . "not_found") (:REASON . "missing"))
 
-

- -

-

[Function]
-get-db-info &key db-name +

[Function]
+get-db-info &key db-name +

+

Returns database information for the connection and database in the current context, or, if the db-name key parameter is specified, for that database. -

+

+

Example: +

 (get-db-info)
 => ((:DB_NAME . "test-db") (:DOC_COUNT . 3) (:UPDATE_SEQ . 4))
 
-

-

[Function]
+

[Function]
list-dbs +

-Returns a list of database names available in the current connection. +

+ Returns a list of database names available in the current connection. +

-

[Function]
-set-connection &key host +

[Function]
+set-connection &key host db-name protocol port => no value +

+

Sets the host name, database name, protocol ("http" or "https") and port number for the top-level connection to the CouchDb server. Default connection settings are host="localhost", protocol="http", port="5984" and database="default". -

-See (with-connection) +

+

See (with-connection)

-

[Macro]
-with-connection (&key host -db-name protocol port) &body body => value returned by body +

[Macro]
+with-connection (&key host +db-name protocol port) &body body => value returned by body +

+

Executes the contained statements in the context of any of the specified connection values. Sets the host name, database name, protocol ("http" or "https") or port number of the CouchDb server to use in the expressions in the body. -

+

+

Example: +

 ;; Get document from specified host and database
 (with-connection (:host "cornichon.cucumber.net" :db-name "rfc")
@@ -469,11 +502,9 @@
     (get-document "someid"))
   :id "copy-of-someid")
 
-

-See (set-connection) +

See (set-connection)

-

Document API

@@ -488,6 +519,7 @@

Special Properties +

When a document is created CouchDb assigns special properties to that @@ -495,7 +527,7 @@ properties include the document's ID (:_id) and the document revision number (:_rev). All special properties begin with an underscore (_) symbol. -

+

 (create-document '((:name . "Maxwell Smart") (:agent . 86)) :id "max")

[416 lines skipped]



From peddy at common-lisp.net  Fri Dec 14 23:22:58 2007
From: peddy at common-lisp.net (peddy)
Date: Fri, 14 Dec 2007 18:22:58 -0500 (EST)
Subject: [clouchdb-cvs] CVS clouchdb/src
Message-ID: <20071214232258.D727B5E006@common-lisp.net>

Update of /project/clouchdb/cvsroot/clouchdb/src
In directory clnet:/tmp/cvs-serv19826/src

Modified Files:
	clouchdb.lisp examples.lisp tests.lisp 
Log Message:
Fixed document ID encoding bug, documentation now valid XHMTL

--- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp	2007/12/09 16:03:21	1.3
+++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp	2007/12/14 23:22:58	1.4
@@ -161,6 +161,19 @@
   "Wrap specified value in double quotes."
   (cat "\"" value "\""))
 
+(defun url-encode (string)
+  "URL-encode a string."
+  (with-output-to-string (s)
+    (loop for c across string
+          do (cond ((or (char<= #\0 c #\9)
+                        (char<= #\a c #\z)
+                        (char<= #\A c #\Z)
+                        (find c "$-_.!*'()," :test #'char=))
+                     (write-char c s))
+                   ((char= c #\Space)
+                     (write-char #\+ s))
+                   (t (format s "%~2,'0x" (char-code c)))))))
+
 (defun make-uri (&rest rest)
   (concatenate 'string *protocol* "://" *host* ":" *port* "/"
 	       (apply #'concatenate 'string rest)))
@@ -222,7 +235,7 @@
 	(t (cdr (assoc name doc)))))
 
 (defun (setf document-property) (value name doc)
-  "Allows setting of document properties in place"
+  "Allows setting of document properties in place."
   (cond ((hash-table-p doc)
 	 (setf (gethash name doc) value))
 	(t (rplacd (assoc name doc) value)))
@@ -290,7 +303,7 @@
 error condition is generated. Specify :recreate to potentially delete
 and create a new database."
   (let* ((name (if db-name-p db-name *db-name*))
-	 (res (db-request (cat name "/") :method :put)))
+	 (res (db-request (cat (url-encode name) "/") :method :put)))
     (if (equal "database_already_exists" (document-property :error res))
       (ecase if-exists
 	((:ignore) (list (cons :ok t) (cons :ignored t)))
@@ -311,7 +324,7 @@
 error condition, but this can be avoided by specifying :ignore in the
 if-missing parameter."
   (let* ((name (if db-name-p db-name *db-name*))
-	 (res (db-request (cat name "/") :method :delete)))
+	 (res (db-request (cat (url-encode name) "/") :method :delete)))
     (if (and (document-property :error res) (not (eq :ignore if-missing)))
 	(restart-case 
 	    (error 'db-does-not-exist
@@ -324,7 +337,7 @@
   "Get information for named database, or couchdb server if no
 database specified."
   (let ((dbn (if db-name-p db-name *db-name*)))
-    (db-request (cat dbn "/") :method :get)))
+    (db-request (cat (url-encode dbn) "/") :method :get)))
 
 (defun create-temp-db-name ()
   "Return a database name that's probably unique."  
@@ -362,7 +375,7 @@
 ascending ID order by default, or descending order of descending
 parameter is non-nil."
   (ensure-db ()
-    (db-request (cat *db-name* "/_all_docs") 
+    (db-request (cat (url-encode *db-name*) "/_all_docs") 
 		:method :get
 		:parameters (if descending
 				;; ?descending=false causes error ATM
@@ -386,7 +399,7 @@
       (push (cons "revs" "true") parameters))
     (when revision-info
       (push (cons "revs_info" "true") parameters))
-    (let ((res (ensure-db () (db-request (cat *db-name* "/" id)
+    (let ((res (ensure-db () (db-request (cat (url-encode *db-name*) "/" (url-encode id))
 					 :method :get 
 					 :parameters parameters))))
       (if (document-property :error res)
@@ -413,7 +426,9 @@
 	  ((and id current-id (not (equal current-id id)))
 	   (setf doc (document-properties doc))))
     (let ((res (ensure-db ()
-		 (db-request (cat *db-name* "/" (if id id current-id))
+		 (db-request (cat (url-encode *db-name*) "/" (url-encode (if id id current-id)))
+                             :external-format-out :utf-8
+                             :content-type "text/json"
 			     :method :put :content (document-to-json doc)))))
       (when (document-property :error res)
 	(error (if (equal "conflict" (document-property :error res)) 
@@ -422,12 +437,11 @@
 	       :reason (document-property :reason res)))
       res)))
 		      
-
 (defun post-document (doc)
   "Create a document and let the server assign an ID. A successful
 areturn value includes the new document ID, in the :ID property."
   (ensure-db ()
-    (db-request (cat *db-name* "/")
+    (db-request (cat (url-encode *db-name*) "/")
 		:method :post
 		:content (document-to-json doc))))
 
@@ -443,7 +457,7 @@
 should be a list of documents. Each document in the list may be in the
 form of a hash table or an associative list."
   (ensure-db () 
-    (db-request (cat *db-name* "/_bulk_docs")
+    (db-request (cat (url-encode *db-name*) "/_bulk_docs")
 		:method :post
 		:content-type "text/javascript"
 		:content 
@@ -458,7 +472,7 @@
 not the revision, the current document will be fetched and it's
 revision number will be used for the delete."
   (labels ((del (id rev)
-	     (db-request (cat *db-name* "/" id "?rev=" rev)
+	     (db-request (cat (url-encode *db-name*) "/" (url-encode id) "?rev=" rev)
 			 :method :delete)))
     (cond ((not (null document))
 	   (delete-document :id (document-property :_id document)
@@ -476,7 +490,7 @@
                     end-key count update descending skip)
   "Execute query using an ad-hoc view."
   (ensure-db ()
-    (db-request (cat *db-name* "/_temp_view")
+    (db-request (cat (url-encode *db-name*) "/_temp_view")
 		:method :post
 		:content-type "text/javascript"
                 :parameters (transform-params options *view-options*)
@@ -493,7 +507,7 @@
 			   (if (not (null (cdr views))) ", ")
 			   (mk-view-js (cdr views))))))))
     (ensure-db ()
-      (db-request (cat *db-name* "/_design/" id)
+      (db-request (cat (url-encode *db-name*) "/_design/" (url-encode id))
 		  :method :put
 		  :content
 		  (cat "{\"language\" : \"text/javascript\"," 
@@ -502,7 +516,7 @@
 (defun delete-view (id &key revision)
   "Delete identified view document"
   (ensure-db ()
-    (delete-document :id (cat "_design/" id) :revision revision)))
+    (delete-document :id (cat "_design/" (url-encode id)) :revision revision)))
 
 (defun invoke-view (id view &rest options &key key start-key start-key-docid
                     end-key count update descending skip)
@@ -516,6 +530,6 @@
 inconsistency."
   (declare (ignore key start-key start-key-docid end-key count update descending skip))
   (ensure-db ()
-    (db-request (cat *db-name* "/_view/" id "/" view)
+    (db-request (cat (url-encode *db-name*) "/_view/" (url-encode id) "/" (url-encode view))
 		:method :get
                 :parameters (transform-params options *view-options*))))
--- /project/clouchdb/cvsroot/clouchdb/src/examples.lisp	2007/12/09 16:03:21	1.2
+++ /project/clouchdb/cvsroot/clouchdb/src/examples.lisp	2007/12/14 23:22:58	1.3
@@ -100,7 +100,6 @@
     (create-document '(("name" . "Czech Republic")
 		       ("tags" . ("country" "european"))
 		       ("motto" . "Truth prevails")
-
 		       ("demographics" . ((:population . 10230000)
 					  ;; A nested map property:
 					  (:religion . ((:agnostic . 0.59)
--- /project/clouchdb/cvsroot/clouchdb/src/tests.lisp	2007/12/09 16:03:21	1.2
+++ /project/clouchdb/cvsroot/clouchdb/src/tests.lisp	2007/12/14 23:22:58	1.3
@@ -330,6 +330,12 @@
 		       (return-from test nil)))
 	      t))))
 
+(addtest (clouchdb-doc-api-tests)
+  (:documentation "Test document ID encoding")
+  encode-document-id
+  (ensure (document-property :ok (create-document '((:a "test")) :id "http://google.com")))
+  (ensure-same (document-property :_id (get-document "http://google.com")) "http://google.com"))
+
 ;;
 ;; View API Tests
 ;;



From peddy at common-lisp.net  Mon Dec 17 13:58:32 2007
From: peddy at common-lisp.net (peddy)
Date: Mon, 17 Dec 2007 08:58:32 -0500 (EST)
Subject: [clouchdb-cvs] CVS clouchdb/public_html
Message-ID: <20071217135832.749C5620C7@common-lisp.net>

Update of /project/clouchdb/cvsroot/clouchdb/public_html
In directory clnet:/tmp/cvs-serv4983/public_html

Modified Files:
	index.html 
Log Message:
  - Encode all URL parameters properly, this fixes issues reported by
  Daniel Farina having to do with the inability to use leagal
  characters in document IDs.
  - Error handling for (post-document)


--- /project/clouchdb/cvsroot/clouchdb/public_html/index.html	2007/12/14 23:22:58	1.3
+++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html	2007/12/17 13:58:32	1.4
@@ -518,15 +518,16 @@
 

-Special Properties + Special Properties

+

-When a document is created CouchDb assigns special properties to that -document, these properties cannot be modified by clients. The special -properties include the document's ID (:_id) and the document revision -number (:_rev). All special properties begin with an underscore (_) -symbol. + When a document is created CouchDb assigns special properties to that + document, these properties cannot be modified by clients. The special + properties include the document's ID (:_id) and the document revision + number (:_rev). All special properties begin with an underscore (_) + symbol.




From peddy at common-lisp.net  Mon Dec 17 13:58:32 2007
From: peddy at common-lisp.net (peddy)
Date: Mon, 17 Dec 2007 08:58:32 -0500 (EST)
Subject: [clouchdb-cvs] CVS clouchdb/src
Message-ID: <20071217135832.B6E186A004@common-lisp.net>

Update of /project/clouchdb/cvsroot/clouchdb/src
In directory clnet:/tmp/cvs-serv4983/src

Modified Files:
	changelog.txt clouchdb.asd clouchdb.lisp package.lisp 
Log Message:
  - Encode all URL parameters properly, this fixes issues reported by
  Daniel Farina having to do with the inability to use leagal
  characters in document IDs.
  - Error handling for (post-document)


--- /project/clouchdb/cvsroot/clouchdb/src/changelog.txt	2007/12/09 16:03:21	1.2
+++ /project/clouchdb/cvsroot/clouchdb/src/changelog.txt	2007/12/17 13:58:32	1.3
@@ -1,4 +1,10 @@
 
+0.0.5:
+  - Encode all URL parameters properly, this fixes issues reported by
+  Daniel Farina having to do with the inability to use leagal
+  characters in document IDs.
+  - Error handling for (post-document)
+
 0.0.4:
 
   - Fixed ad-hoc-view and invoke-view functions so that they now use
--- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.asd	2007/12/01 14:19:59	1.1.1.1
+++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.asd	2007/12/17 13:58:32	1.2
@@ -38,7 +38,8 @@
   :serial t
   :version #.*clouchdb-version*
   :depends-on (:drakma
-               :cl-json)
+               :cl-json
+               :flexi-streams)
   :components ((:file "package")
                (:file "clouchdb")))
 
--- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp	2007/12/14 23:22:58	1.4
+++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp	2007/12/17 13:58:32	1.5
@@ -25,7 +25,8 @@
 (in-package :clouchdb)
 
 (defvar *host* "localhost" "CouchDb server host name")
-(defvar *port* "5984" "As of version 7.2, the IANA assigned CouchDb port (was 8888)")
+(defvar *port* "5984" 
+  "As of version 7.2, the IANA assigned CouchDb port (was 8888)")
 (defvar *db-name* "default" "Default database name")
 (defvar *protocol* "http" "http or https")
 
@@ -37,6 +38,9 @@
 
 (defparameter *temp-db-counter* 0 "Used in the creation of temporary databases")
 
+;(defconstant +utf-8+ (make-external-format :utf-8 :eol-style :lf)
+;  "Default external format for document content.")
+
 ;;
 ;; URL Parameter helpers
 ;;
@@ -104,7 +108,11 @@
 (define-condition doc-error (error) 
   ((text :initarg :uri :reader text)
    (reason :initarg :reason :reader reason)
-   (id :initarg :id :reader id)))
+   (id :initarg :id :reader id))
+  (:report (lambda (condition stream)
+	     (format stream "Reason \"~A\", Document ID: \"~A\""
+		     (reason condition)
+		     (id condition)))))
   
 (define-condition id-or-revision-conflict (doc-error) 
   ()
@@ -197,6 +205,14 @@
 		      :uri (make-uri dbn)))))
        ,result)))
 
+;; (defmacro handle-doc-errors (&body body)
+;;   (let ((result (gensym)))
+;;     `(let ((,result (progn , at body)))
+;;        (when (document-property :error (,result))
+;;          (cond ((equal "conflict" (document-property :error result))
+;;                 (error 'id-or-revision-conflict 
+               
+
 ;;
 ;;
 ;;
@@ -235,7 +251,7 @@
 	(t (cdr (assoc name doc)))))
 
 (defun (setf document-property) (value name doc)
-  "Allows setting of document properties in place."
+  "Allows setting of document properties in place (destructively)."
   (cond ((hash-table-p doc)
 	 (setf (gethash name doc) value))
 	(t (rplacd (assoc name doc) value)))
@@ -303,7 +319,8 @@
 error condition is generated. Specify :recreate to potentially delete
 and create a new database."
   (let* ((name (if db-name-p db-name *db-name*))
-	 (res (db-request (cat (url-encode name) "/") :method :put)))
+	 (res (db-request (cat (url-encode name) "/") 
+                          :method :put)))
     (if (equal "database_already_exists" (document-property :error res))
       (ecase if-exists
 	((:ignore) (list (cons :ok t) (cons :ignored t)))
@@ -426,9 +443,10 @@
 	  ((and id current-id (not (equal current-id id)))
 	   (setf doc (document-properties doc))))
     (let ((res (ensure-db ()
-		 (db-request (cat (url-encode *db-name*) "/" (url-encode (if id id current-id)))
-                             :external-format-out :utf-8
+		 (db-request (cat (url-encode *db-name*) "/" 
+                                  (url-encode (if id id current-id)))
                              :content-type "text/json"
+;;                             :external-format-out +utf-8+
 			     :method :put :content (document-to-json doc)))))
       (when (document-property :error res)
 	(error (if (equal "conflict" (document-property :error res)) 
@@ -440,10 +458,23 @@
 (defun post-document (doc)
   "Create a document and let the server assign an ID. A successful
 areturn value includes the new document ID, in the :ID property."
-  (ensure-db ()
-    (db-request (cat (url-encode *db-name*) "/")
-		:method :post
-		:content (document-to-json doc))))
+  (let ((res (ensure-db ()
+               (db-request (cat (url-encode *db-name*) "/")
+                           :method :post
+                           :content (document-to-json doc)))))
+    (when (document-property :error res)
+      (error 'doc-error) :id nil :reason (document-property :reason res))
+    res))
+
+;; (defun post-document (doc)
+;;   "Create a document and let the server assign an ID. A successful
+;; areturn value includes the new document ID, in the :ID property."
+;;   (ensure-db ()
+;;     (db-request (cat (url-encode *db-name*) "/")
+;; 		:method :post
+;;                 :content-type "text/plain;charset=utf-8"
+;;                 :external-format-out +utf-8+
+;; 		:content #p"/Users/peter/encodings.txt")))
 
 (defun create-document (doc &key id)
   "Create a new document, optionally specifying the new document
--- /project/clouchdb/cvsroot/clouchdb/src/package.lisp	2007/12/01 14:19:59	1.1.1.1
+++ /project/clouchdb/cvsroot/clouchdb/src/package.lisp	2007/12/17 13:58:32	1.2
@@ -25,7 +25,7 @@
 (cl:in-package :cl-user)
 
 (defpackage :clouchdb
-  (:use :cl :drakma :json)
+  (:use :cl :drakma :json :flexi-streams)
   (:export :*scheme*
 	   :*host*
 	   :*port*



From peddy at common-lisp.net  Mon Dec 17 23:18:08 2007
From: peddy at common-lisp.net (peddy)
Date: Mon, 17 Dec 2007 18:18:08 -0500 (EST)
Subject: [clouchdb-cvs] CVS clouchdb/src
Message-ID: <20071217231808.1BFB27E007@common-lisp.net>

Update of /project/clouchdb/cvsroot/clouchdb/src
In directory clnet:/tmp/cvs-serv3658/src

Modified Files:
	tests.lisp 
Log Message:
Fixed utf-8 encoding bug


--- /project/clouchdb/cvsroot/clouchdb/src/tests.lisp	2007/12/14 23:22:58	1.3
+++ /project/clouchdb/cvsroot/clouchdb/src/tests.lisp	2007/12/17 23:18:07	1.4
@@ -336,6 +336,45 @@
   (ensure (document-property :ok (create-document '((:a "test")) :id "http://google.com")))
   (ensure-same (document-property :_id (get-document "http://google.com")) "http://google.com"))
 
+(addtest (clouchdb-doc-api-tests)
+  (:documentation "Test document content encoding by creating a
+  document with a field for a variety of languages, then fetching that
+  document and comparing the fecthed data with the source data") 
+  encode-document-contents-glass-eating
+  (ensure
+   (let ((glass-eaters '((:middle-english . "An preost wes on leoden, La??amon was ihoten")
+                         (:classical-greek . "??????????? ????????????? ????????????????? ??????????? ????? ???? ???????????????.")
+                         (:monotonic-greek . "?????????? ???? ?????? ???????????????? ???????????? ?????????? ???? ???????? ????????????.")
+                         (:polytonic-greek . "??????????? ????? ?????? ???????????????? ????????????? ??????????? ????? ???????? ????????????.")
+                         (:french . "Je peux manger du verre, ??a ne me fait pas de mal.")
+                         (:proven??al . "P??di manjar de veire, me nafrari?? pas.")
+                         (:walloon . "Dji pou magn?? do v??re, ??oula m' freut n??n m??.")
+                         (:spanish . "Puedo comer vidrio, no me hace da??o.")
+                         (:romanian . "Pot s?? m??n??nc sticl?? ??i ea nu m?? r??ne??te.")
+                         (:esperanto . "Mi povas man??i vitron, ??i ne dama??as min.")
+                         (:czech . "Mohu j??st sklo, neubl?????? mi.")
+                         (:lithuanian . "A?? galiu valgyti stikl?? ir jis man??s ne??eid??ia")
+                         (:polska . "Mog?? je???? szk??o i mi nie szkodzi.")
+                         (:macedonian . "?????????? ???? ?????????? ????????????, ?? ???? ???? ??????????.")
+                         (:russian . "?? ???????? ???????? ????????????, ?????? ?????? ???? ????????????.")
+                         (:belarusian-cyrillic . "?? ???????? ???????? ????????, ?????? ?????? ???? ????????????????.")
+                         (:belarusian-lacinka . "Ja mahu je??ci ??k??o, jano mne ne ??kodzi??.")
+                         (:armenian . "?????????? ?????????? ?????????? ?? ???????? ?????????????????? ????????????")
+                         (:hebrew . "?????? ???????? ?????????? ???????????? ?????? ???? ???????? ????")
+                         (:yiddish . "?????? ?????? ?????? ?????????? ?????? ???? ?????? ?????? ???????? ????")
+                         (:chinese . "????????????????????????????????????")
+                         (:chinese-traditional . "????????????????????????????????????")
+                         (:japanese . "???????????????????????????????????????????????????????????????????????????")
+                         (:korean . "?????? ????????? ?????? ??? ?????????. ????????? ????????? ?????????")
+                         (:euro-symbol . "???")
+                         (:georgian . "??????????????? ???????????? ?????? ????????? ??????????????????."))))
+     (and (document-property :ok (create-document glass-eaters :id "glass-eaters"))
+          (let ((doc (get-document "glass-eaters")))
+            (reduce #'(lambda (a b) (and a b))
+                    (mapcar #'(lambda (e) 
+                                (equal (cdr e)
+                                       (document-property (car e) doc)))
+                            doc)))))))
 ;;
 ;; View API Tests
 ;;



From peddy at common-lisp.net  Mon Dec 17 23:22:23 2007
From: peddy at common-lisp.net (peddy)
Date: Mon, 17 Dec 2007 18:22:23 -0500 (EST)
Subject: [clouchdb-cvs] CVS clouchdb/src
Message-ID: <20071217232223.732F37E007@common-lisp.net>

Update of /project/clouchdb/cvsroot/clouchdb/src
In directory clnet:/tmp/cvs-serv5674/src

Modified Files:
	examples.lisp clouchdb.lisp changelog.txt 
Log Message:
Fixed utf-8 encoding


--- /project/clouchdb/cvsroot/clouchdb/src/examples.lisp	2007/12/14 23:22:58	1.3
+++ /project/clouchdb/cvsroot/clouchdb/src/examples.lisp	2007/12/17 23:22:23	1.4
@@ -55,9 +55,7 @@
     (create-db :if-exists :recreate)
     ;; Create a document with ID of "haussmann"
     (create-document '(;; Fields with scalar values:
-		       (:name . "Georges-Eugene Haussmann") 
-		       ;; Note: above should be "Georges-Eug?ne" but
-		       ;; there's a character handling bug somewhere
+		       (:name . "Georges-Eug?ne Haussmann") 
 		       (:aka . "Baron Haussmann")
 		       (:born . "1809-03-27")
 		       (:died . "1891-01-11"))
--- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp	2007/12/17 13:58:32	1.5
+++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp	2007/12/17 23:22:23	1.6
@@ -38,8 +38,8 @@
 
 (defparameter *temp-db-counter* 0 "Used in the creation of temporary databases")
 
-;(defconstant +utf-8+ (make-external-format :utf-8 :eol-style :lf)
-;  "Default external format for document content.")
+(defconstant +utf-8+ (make-external-format :utf-8 :eol-style :lf)
+  "Default external format for document content.")
 
 ;;
 ;; URL Parameter helpers
@@ -212,6 +212,8 @@
 ;;          (cond ((equal "conflict" (document-property :error result))
 ;;                 (error 'id-or-revision-conflict 
                
+(defun test ()
+  (with-open-file 
 
 ;;
 ;;
@@ -445,9 +447,11 @@
     (let ((res (ensure-db ()
 		 (db-request (cat (url-encode *db-name*) "/" 
                                   (url-encode (if id id current-id)))
-                             :content-type "text/json"
-;;                             :external-format-out +utf-8+
-			     :method :put :content (document-to-json doc)))))
+                             :content-type "text/javascript"
+                             :external-format-out +utf-8+
+                             :content-length nil
+                             :content (document-to-json doc)
+			     :method :put))))
       (when (document-property :error res)
 	(error (if (equal "conflict" (document-property :error res)) 
 		   'id-or-revision-conflict 'doc-error)
@@ -460,22 +464,15 @@
 areturn value includes the new document ID, in the :ID property."
   (let ((res (ensure-db ()
                (db-request (cat (url-encode *db-name*) "/")
-                           :method :post
-                           :content (document-to-json doc)))))
+                           :content-type "text/javascript"
+                           :external-format-out +utf-8+
+                           :content-length nil
+                           :content (document-to-json doc)
+                           :method :post))))
     (when (document-property :error res)
       (error 'doc-error) :id nil :reason (document-property :reason res))
     res))
 
-;; (defun post-document (doc)
-;;   "Create a document and let the server assign an ID. A successful
-;; areturn value includes the new document ID, in the :ID property."
-;;   (ensure-db ()
-;;     (db-request (cat (url-encode *db-name*) "/")
-;; 		:method :post
-;;                 :content-type "text/plain;charset=utf-8"
-;;                 :external-format-out +utf-8+
-;; 		:content #p"/Users/peter/encodings.txt")))
-
 (defun create-document (doc &key id)
   "Create a new document, optionally specifying the new document
 ID."
@@ -490,7 +487,9 @@
   (ensure-db () 
     (db-request (cat (url-encode *db-name*) "/_bulk_docs")
 		:method :post
-		:content-type "text/javascript"
+                :content-type "application/xml"
+                :external-format-out +utf-8+
+                :content-length nil
 		:content 
                 (cat "[ " 
 		     (string-join (mapcar #'document-to-json docs)) 
@@ -503,7 +502,8 @@
 not the revision, the current document will be fetched and it's
 revision number will be used for the delete."
   (labels ((del (id rev)
-	     (db-request (cat (url-encode *db-name*) "/" (url-encode id) "?rev=" rev)
+	     (db-request (cat (url-encode *db-name*) "/" (url-encode id) "?rev=" 
+                              (url-encode (value-as-string rev)))
 			 :method :delete)))
     (cond ((not (null document))
 	   (delete-document :id (document-property :_id document)
@@ -523,7 +523,9 @@
   (ensure-db ()
     (db-request (cat (url-encode *db-name*) "/_temp_view")
 		:method :post
+                :external-format-out +utf-8+
 		:content-type "text/javascript"
+                :content-length nil
                 :parameters (transform-params options *view-options*)
 		:content view)))
 
@@ -540,6 +542,9 @@
     (ensure-db ()
       (db-request (cat (url-encode *db-name*) "/_design/" (url-encode id))
 		  :method :put
+                  :external-format-out +utf-8+
+                  :content-type "text/javascript"
+                  :content-length nil
 		  :content
 		  (cat "{\"language\" : \"text/javascript\"," 
 		       "\"views\" : {" (mk-view-js view-defs) "}}")))))
--- /project/clouchdb/cvsroot/clouchdb/src/changelog.txt	2007/12/17 13:58:32	1.3
+++ /project/clouchdb/cvsroot/clouchdb/src/changelog.txt	2007/12/17 23:22:23	1.4
@@ -1,4 +1,7 @@
 
+0.0.6:
+  - Finally fixed utf-8 encoding
+
 0.0.5:
   - Encode all URL parameters properly, this fixes issues reported by
   Daniel Farina having to do with the inability to use leagal



From peddy at common-lisp.net  Mon Dec 17 23:22:23 2007
From: peddy at common-lisp.net (peddy)
Date: Mon, 17 Dec 2007 18:22:23 -0500 (EST)
Subject: [clouchdb-cvs] CVS clouchdb/public_html
Message-ID: <20071217232223.AE1197E007@common-lisp.net>

Update of /project/clouchdb/cvsroot/clouchdb/public_html
In directory clnet:/tmp/cvs-serv5674/public_html

Modified Files:
	index.html 
Log Message:
Fixed utf-8 encoding


--- /project/clouchdb/cvsroot/clouchdb/public_html/index.html	2007/12/17 13:58:32	1.4
+++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html	2007/12/17 23:22:23	1.5
@@ -43,11 +43,16 @@
 

News

    +
  • Dec 17, 2007 Released version 0.0.5 with fixes for + encoding of URL parameters. This fix allows use of leagl CouchDb + characaters for database names and document IDs which must be + escaped in urls. +
  • Dec 9, 2007 - Version 0.0.4: Updated (invoke-view) and (ad-hoc-view) to use all options - supported by corresponding CouchDb API. Somehow I'd missed - these before. + supported by corresponding CouchDb API. Somehow I'd missed these + before.
  • Nov 28, 2007 - CouchDb 7.2 now uses IANA assigned port 5984 instead of 8888. From peddy at common-lisp.net Tue Dec 18 02:16:02 2007 From: peddy at common-lisp.net (peddy) Date: Mon, 17 Dec 2007 21:16:02 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071218021602.97AD01092@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv31727/src Modified Files: clouchdb.lisp Log Message: Updated document API documentation Removed junk code --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/17 23:22:23 1.6 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 02:16:02 1.7 @@ -212,13 +212,6 @@ ;; (cond ((equal "conflict" (document-property :error result)) ;; (error 'id-or-revision-conflict -(defun test () - (with-open-file - -;; -;; -;; - (defun document-to-json (doc) "Convert document data, the top-level of wich is either an associative list or hashtable, to json data" From peddy at common-lisp.net Tue Dec 18 02:16:02 2007 From: peddy at common-lisp.net (peddy) Date: Mon, 17 Dec 2007 21:16:02 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/public_html Message-ID: <20071218021602.CFE44405F@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/public_html In directory clnet:/tmp/cvs-serv31727/public_html Modified Files: index.html Log Message: Updated document API documentation Removed junk code --- /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/17 23:22:23 1.5 +++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/18 02:16:02 1.6 @@ -37,6 +37,11 @@
  • Examples

  • Support and mailing lists

  • API Reference

  • +
  • Symbol Index

@@ -366,7 +371,7 @@

API Reference

-

Server Connection and Database Management API

+

Server Connection and Database Management API

The API described in this has to do with managing CouchDb server @@ -510,17 +515,71 @@

See (set-connection)

-

Document API

+

Document API

+

+ The Document ID +

+
+

+ Documents in clouchdb are identified by a document ID string which + must be unique within the database that will contain the + document. The ID string may either be specified when the document + is created or it can be provided by the CouchDb server. +

+

- Documents in clouchdb are identified by a document ID, their content - takes the form of an associative list. The native document - representation in CouchDb is a JSON - object. Using - cl-json, - clouchdb automatically translates documents between JSON and Lisp - associative lists. + Document Content

+
+

+ Document content takes the form of an associative list. The car of + each element of the associative list may be either a string or a + symbol. For example, each of the following calls to + (create-document) creates a document with a field named "name": +

+ +
+(create-document '((:name . "Max"))
+(create-document '((name . "Max"))
+(create-document '(("name" . "Max"))
+
+ +

+ Field names in CouchDb are case sensitive. To specify a field name + that uses upper case characters, you must use a string value as + symbols are always converted to lower case. In the following + example a single document is created with two fields, "name" and + "Name": +

+ +
+(create-document '((:name . "Max")
+                   ("Name" . "Max")))
+
+ +

+ The native document representation in the CouchDb protocol is + a JSON object. Using + cl-json, + clouchdb automatically translates documents between JSON and Lisp + associative lists. +

+ +

+ The value of a document field may be a string, a number, a list, + or an associative list. Document field values may be nested to + create very complex document structures. +

+ +
+(create-document '((:string . "String Value")
+                   (:number . 3.1415926535)
+                   (:list . (milk eggs "green beans"))
+                   (:alist . ((
+
+ +

Special Properties @@ -678,7 +737,7 @@


-

Views API

+h4>Views API

Views are the mechanism with which documents are queried in From peddy at common-lisp.net Tue Dec 18 17:26:56 2007 From: peddy at common-lisp.net (peddy) Date: Tue, 18 Dec 2007 12:26:56 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071218172656.00D6143225@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv9477/src Modified Files: tests.lisp clouchdb.lisp Log Message: Support for utf-8 encoded document IDs --- /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/17 23:18:07 1.4 +++ /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/18 17:26:56 1.5 @@ -337,6 +337,17 @@ (ensure-same (document-property :_id (get-document "http://google.com")) "http://google.com")) (addtest (clouchdb-doc-api-tests) + (:documentation "Test encoding and decoding of utf-8 document IDs") + encode-document-utf-8-ids + (ensure + (let ((ids '("??ngstr??m Caf??" "????????????????" "?????????????????????????????????"))) + (reduce #'(lambda (a b) (and a b)) + (mapcar #'(lambda (id) + (and (document-property :ok (create-document nil :id id)) + (equal id (document-property :_id (get-document id))))) + ids))))) + +(addtest (clouchdb-doc-api-tests) (:documentation "Test document content encoding by creating a document with a field for a variety of languages, then fetching that document and comparing the fecthed data with the source data") --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 02:16:02 1.7 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 17:26:56 1.8 @@ -162,27 +162,34 @@ out)) (defmacro cat (&rest rest) - "Silly shorthand for (concatenate 'string)" + "Shorthand for (concatenate 'string)" `(concatenate 'string , at rest)) (defun doublequote (value) "Wrap specified value in double quotes." (cat "\"" value "\"")) +(defun convert-encoding (string encoding) + "Convert string to specified encoding. This may be totally wrong and +probably way too inefficient, but it seems to work." + (octets-to-string (string-to-octets string :external-format encoding))) + (defun url-encode (string) "URL-encode a string." (with-output-to-string (s) - (loop for c across string + (loop for c across (convert-encoding string +utf-8+) do (cond ((or (char<= #\0 c #\9) (char<= #\a c #\z) (char<= #\A c #\Z) (find c "$-_.!*'()," :test #'char=)) (write-char c s)) ((char= c #\Space) - (write-char #\+ s)) + (write-string "%20" s)) (t (format s "%~2,'0x" (char-code c))))))) (defun make-uri (&rest rest) + "Return a URI containing *protocol*://*host*:*port*/ and the +concatenation of the remaining parameters." (concatenate 'string *protocol* "://" *host* ":" *port* "/" (apply #'concatenate 'string rest))) @@ -205,13 +212,6 @@ :uri (make-uri dbn))))) ,result))) -;; (defmacro handle-doc-errors (&body body) -;; (let ((result (gensym))) -;; `(let ((,result (progn , at body))) -;; (when (document-property :error (,result)) -;; (cond ((equal "conflict" (document-property :error result)) -;; (error 'id-or-revision-conflict - (defun document-to-json (doc) "Convert document data, the top-level of wich is either an associative list or hashtable, to json data" @@ -254,7 +254,7 @@ (defun db-request (uri &rest keys &key &allow-other-keys) "Used by all Couchdb APIs to make the actual REST request." -;; (format t "uri: ~S~% keys: ~S~%" (make-uri uri) keys) + ;;(format t "uri: ~S~% keys: ~S~%" (make-uri uri) keys) (let ((*text-content-types* *text-types*)) (multiple-value-bind (body status headers uri stream must-close reason-phrase) (apply #'drakma:http-request (make-uri uri) keys) @@ -304,7 +304,7 @@ ;; (defun list-dbs () - "List all databases" + "Return a list of all databases for the current host and port." (db-request "_all_dbs" :method :get)) (defun create-db (&key (db-name nil db-name-p) (if-exists :fail)) @@ -411,7 +411,8 @@ (push (cons "revs" "true") parameters)) (when revision-info (push (cons "revs_info" "true") parameters)) - (let ((res (ensure-db () (db-request (cat (url-encode *db-name*) "/" (url-encode id)) + (let ((res (ensure-db () (db-request (cat (url-encode *db-name*) "/" + (url-encode id)) :method :get :parameters parameters)))) (if (document-property :error res) @@ -513,6 +514,8 @@ (defun ad-hoc-view (view &rest options &key key start-key start-key-docid end-key count update descending skip) "Execute query using an ad-hoc view." + (declare (ignore key start-key start-key-docid end-key count + update descending skip)) (ensure-db () (db-request (cat (url-encode *db-name*) "/_temp_view") :method :post @@ -557,8 +560,10 @@ returns results in reverse order. If update is t, does not refresh view for query, use for higher performance but possible data inconsistency." - (declare (ignore key start-key start-key-docid end-key count update descending skip)) + (declare (ignore key start-key start-key-docid end-key count + update descending skip)) (ensure-db () - (db-request (cat (url-encode *db-name*) "/_view/" (url-encode id) "/" (url-encode view)) + (db-request (cat (url-encode *db-name*) "/_view/" + (url-encode id) "/" (url-encode view)) :method :get :parameters (transform-params options *view-options*)))) From peddy at common-lisp.net Tue Dec 18 18:03:46 2007 From: peddy at common-lisp.net (peddy) Date: Tue, 18 Dec 2007 13:03:46 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071218180346.0FDBC650DB@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv15160/src Modified Files: clouchdb.lisp Log Message: Fixed (get-db-info) for SBCL when :db-name is specified as null (get server info) --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 17:26:56 1.8 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 18:03:45 1.9 @@ -38,7 +38,13 @@ (defparameter *temp-db-counter* 0 "Used in the creation of temporary databases") -(defconstant +utf-8+ (make-external-format :utf-8 :eol-style :lf) +(defmacro define-constant (name value &optional doc) + "A version of DEFCONSTANT for, cough, /strict/ CL implementations." + ;; See + `(defconstant ,name (if (boundp ',name) (symbol-value ',name) ,value) + ,@(when doc (list doc)))) + +(define-constant +utf-8+ (make-external-format :utf-8 :eol-style :lf) "Default external format for document content.") ;; @@ -212,6 +218,23 @@ :uri (make-uri dbn))))) ,result))) +<<<<<<< clouchdb.lisp +;; (defmacro handle-doc-errors (&body body) +;; (let ((result (gensym))) +;; `(let ((,result (progn , at body))) +;; (when (document-property :error (,result)) +;; (cond ((equal "conflict" (document-property :error result)) +;; (error 'id-or-revision-conflict + +<<<<<<< clouchdb.lisp +;; +;; +;; + +======= +>>>>>>> 1.7 +======= +>>>>>>> 1.8 (defun document-to-json (doc) "Convert document data, the top-level of wich is either an associative list or hashtable, to json data" @@ -349,7 +372,8 @@ "Get information for named database, or couchdb server if no database specified." (let ((dbn (if db-name-p db-name *db-name*))) - (db-request (cat (url-encode dbn) "/") :method :get))) + (db-request (if dbn (cat (url-encode dbn) "/")) + :method :get))) (defun create-temp-db-name () "Return a database name that's probably unique." From peddy at common-lisp.net Tue Dec 18 18:09:53 2007 From: peddy at common-lisp.net (peddy) Date: Tue, 18 Dec 2007 13:09:53 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071218180953.1C35965121@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv15531/src Modified Files: clouchdb.lisp Log Message: Removed vc conflict junk --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 18:03:45 1.9 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 18:09:53 1.10 @@ -218,23 +218,6 @@ :uri (make-uri dbn))))) ,result))) -<<<<<<< clouchdb.lisp -;; (defmacro handle-doc-errors (&body body) -;; (let ((result (gensym))) -;; `(let ((,result (progn , at body))) -;; (when (document-property :error (,result)) -;; (cond ((equal "conflict" (document-property :error result)) -;; (error 'id-or-revision-conflict - -<<<<<<< clouchdb.lisp -;; -;; -;; - -======= ->>>>>>> 1.7 -======= ->>>>>>> 1.8 (defun document-to-json (doc) "Convert document data, the top-level of wich is either an associative list or hashtable, to json data" From peddy at common-lisp.net Tue Dec 18 18:20:09 2007 From: peddy at common-lisp.net (peddy) Date: Tue, 18 Dec 2007 13:20:09 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071218182009.A5B4D1092@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv16625/src Modified Files: changelog.txt Log Message: Updated change log --- /project/clouchdb/cvsroot/clouchdb/src/changelog.txt 2007/12/17 23:22:23 1.4 +++ /project/clouchdb/cvsroot/clouchdb/src/changelog.txt 2007/12/18 18:20:09 1.5 @@ -1,6 +1,9 @@ 0.0.6: - - Finally fixed utf-8 encoding + - Finally fixed utf-8 encoding bug for document contents + - Revised document ID encoding to 1) Support utf-8 characters + properly, and 2) Encode spaces using %20 instead of + + - Updated documentation relating to document API 0.0.5: - Encode all URL parameters properly, this fixes issues reported by From peddy at common-lisp.net Tue Dec 18 21:33:34 2007 From: peddy at common-lisp.net (peddy) Date: Tue, 18 Dec 2007 16:33:34 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071218213334.3008549050@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv15753/src Modified Files: tests.lisp clouchdb.lisp Log Message: Added support to document-property for using strings, keyword symbols or regular symgbols for field name, added tests for this feature --- /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/18 17:26:56 1.5 +++ /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/18 21:33:34 1.6 @@ -89,16 +89,6 @@ (push d results)))))) results)) -;; (defun create-view-test2 () -;; "Create a view that uses a key query param" -;; (create-test-documents *people*) -;; (create-view "key-view" -;; (cons "friendof" -;; (ps (lambda (doc) -;; (with-slots (friends) doc -;; (dolist (friend friends) -;; (map friend doc)))))))) - ;; ;; Database API Tests ;; @@ -115,6 +105,53 @@ (*protocol* "http"))) ;; +;; General tests that do not require a db connection +;; + +(deftestsuite clouchdb-general-tests (clouchdb-tests) () ()) + +(addtest (clouchdb-general-tests) + (:documentation "Test document-property") + general-tests-document-property + (ensure + (let ((doc '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "NaMe")))) + (reduce #'(lambda (a b) (and a b)) + (mapcar #'(lambda (e) + (equal (cdr (assoc (car e) doc)) + (document-property (car e) doc))) + doc))))) + +(addtest (clouchdb-general-tests) + (:documentation "Test document-property using property name strings") + general-tests-document-property-string + (ensure-same "name1" + (document-property "name" '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (ensure-same "name2" + (document-property "Name" '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (ensure-same "name3" + (document-property "NaMe" '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3"))))) + +(addtest (clouchdb-general-tests) + (:documentation "Test document-property using keyword symbols") + general-tests-document-property-keyword + (ensure-same "name1" + (document-property :NAME '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (ensure-same "name2" + (document-property :*NAME '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (ensure-same "name3" + (document-property :*NA-ME'((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3"))))) + +(addtest (clouchdb-general-tests) + (:documentation "Test document-property using non-keyword symbols") + general-tests-document-property-symbol + (ensure-same "name1" + (document-property 'name '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (ensure-same "name2" + (document-property '*name '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (ensure-same "name3" + (document-property '*na-me '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3"))))) + +;; ;; Db Administration Tests ;; ;; Test the APIs that create, delete, and get information about @@ -501,7 +538,8 @@ ;; (defun run-all-tests () - (dolist (suite '(clouchdb-db-admin-tests + (dolist (suite '(clouchdb-general-tests + clouchdb-db-admin-tests clouchdb-doc-api-tests clouchdb-view-tests)) (format t "~S~%" (run-tests :suite suite)))) --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 18:09:53 1.10 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 21:33:34 1.11 @@ -245,18 +245,52 @@ new-doc)) (t doc))) + +(defun camel-case-to-lisp (string) + "Converts a string in camelCase to the same lisp-friendly syntax used in parenscript. +Stolen from the cl-json library since it's not exported. Examples: +\"camelCase\" -> \"CAMEL-CASE\", \"CamelCase\" -> \"*CAMEL-CASE\", +\"dojo.widget.TreeNode\" -> \"DOJO.WIDGET.*TREE-NODE\"" + (with-output-to-string (out) + (loop for ch across string + with last-char do + (if (upper-case-p ch) + (progn + (if (and last-char (lower-case-p last-char)) + (write-char #\- out) + (write-char #\* out)) + (write-char ch out)) + (write-char (char-upcase ch) out)) + (setf last-char ch)))) + +(defun as-keyword-symbol (value) + "Return value in a form that would be used to identify the car of a +value in a document. For example, a value of \"FIELD-NAME\" would +return :FIELD-NAME, 'FIELD-NAME would become :FIELD-NAME, and +\"Field-Name\" would become \":*FIELD-NAME\"." + (cond ((keywordp value) + value) + ((stringp value) + (intern (camel-case-to-lisp value) "KEYWORD")) + ((symbolp value) + (as-keyword-symbol (intern (symbol-name value) "KEYWORD"))))) + (defun document-property (name doc) - "Get the value associated with the document property or nil" - (cond ((hash-table-p doc) - (gethash name doc)) - (t (cdr (assoc name doc))))) + "Get the value associated with the document property or nil if there +is no associated value. Note that name may be either a keyword symbol, +a regular symbol or a string." + (let ((name (as-keyword-symbol name))) + (cond ((hash-table-p doc) + (gethash name doc)) + (t (cdr (assoc name doc)))))) (defun (setf document-property) (value name doc) "Allows setting of document properties in place (destructively)." - (cond ((hash-table-p doc) - (setf (gethash name doc) value)) - (t (rplacd (assoc name doc) value))) - value) + (let ((name (as-keyword-symbol name))) + (cond ((hash-table-p doc) + (setf (gethash name doc) value)) + (t (rplacd (assoc name doc) value))) + value)) (defun db-request (uri &rest keys &key &allow-other-keys) "Used by all Couchdb APIs to make the actual REST request." From peddy at common-lisp.net Wed Dec 19 15:10:43 2007 From: peddy at common-lisp.net (peddy) Date: Wed, 19 Dec 2007 10:10:43 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/public_html Message-ID: <20071219151043.F3E855557A@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/public_html In directory clnet:/tmp/cvs-serv18123/public_html Modified Files: index.html Log Message: Updated documentation for 0.0.6 release --- /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/18 02:16:02 1.6 +++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/19 15:10:43 1.7 @@ -43,11 +43,20 @@

  • Views API

  • Symbol Index

  • +
  • Issues and Bugs

  • News

    @@ -736,8 +775,7 @@

    See (create-document) (post-document)

    -
    -h4>Views API +

    Views API

    Views are the mechanism with which documents are queried in @@ -983,6 +1021,47 @@

    +

    Issues and Bugs

    +

    + CouchDb is a young database server, it is currently in development + and not yet feature complete nor are some aspects of its + functionality even fully designed or finalized yet. Clouchdb is a + new library supporting this database, and it too is still in + development. As a result, changes to and bugs in the Clouchdb API + should not be unexpected, though I do strive to minimise both. +

    + +

    + With that in mind, any problems not outlined below should be brought + to the author's attention through + the clouchdb-devel email list. +

    + +

    + Currently known issues: +

    + + +

    Back to Common-lisp.net.

    @@ -995,6 +1074,5 @@ alt="Valid XHTML 1.0 Strict" height="31" width="88" />

    - From peddy at common-lisp.net Wed Dec 19 15:45:00 2007 From: peddy at common-lisp.net (peddy) Date: Wed, 19 Dec 2007 10:45:00 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/public_html Message-ID: <20071219154500.052F872095@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/public_html In directory clnet:/tmp/cvs-serv27061 Modified Files: index.html Log Message: Fixed spelling errors --- /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/19 15:10:43 1.7 +++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/19 15:45:00 1.8 @@ -58,8 +58,8 @@ decoded.
  • Dec 17, 2007 Released version 0.0.5 with fixes for - encoding of URL parameters. This fix allows use of leagl CouchDb - characaters for database names and document IDs which must be + encoding of URL parameters. This fix allows use of legal CouchDb + characters for database names and document IDs which must be escaped in urls.
  • Dec 9, 2007 - Version 0.0.4: Updated @@ -545,7 +545,7 @@ Document content takes the form of an associative list. The car of each element of the associative list may be either a string or a symbol. For example, each of the following calls to - (create-document) creates a document with a field wich will be + (create-document) creates a document with a field which will be named "name" in the database, but which will be :NAME (a keyword symbol) when the documents are retrieved:

    @@ -567,7 +567,7 @@

    Field names in CouchDb are case sensitive. To specify a field name that uses mixed case, you may either use a string value, or encode - the symbol approprately. In the following example a single + the symbol appropriately. In the following example a single document is created with two fields named, "name" and "Name":

    From peddy at common-lisp.net Thu Dec 20 23:40:29 2007 From: peddy at common-lisp.net (peddy) Date: Thu, 20 Dec 2007 18:40:29 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071220234029.B9C0E3C088@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv30013/src Modified Files: clouchdb.lisp Log Message: Switched to using custom document encoder --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/18 21:33:34 1.11 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/20 23:40:29 1.12 @@ -221,10 +221,12 @@ (defun document-to-json (doc) "Convert document data, the top-level of wich is either an associative list or hashtable, to json data" - (cond ((listp doc) - (json:encode-json-alist-to-string doc)) - (t - (json:encode-json-to-string doc)))) + (encode-document doc)) + +;; (cond ((listp doc) +;; (json:encode-json-alist-to-string doc)) +;; (t +;; (json:encode-json-to-string doc)))) (defun document-as-hash (doc) "Convert a document to a hashtable if it isn't one already. Document From peddy at common-lisp.net Thu Dec 20 23:43:33 2007 From: peddy at common-lisp.net (peddy) Date: Thu, 20 Dec 2007 18:43:33 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071220234333.A831E3C088@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv30888/src Added Files: encoder.lisp Log Message: Switched to using custom document encoder --- /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/20 23:43:33 NONE +++ /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/20 23:43:33 1.1 ;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CLOUCHDB; Base: 10 -*- ;;; Copyright (c) 2007 Peter Eddy. All rights reserved. ;;; Permission is hereby granted, free of charge, to any person ;;; obtaining a copy of this software and associated documentation ;;; files (the "Software"), to deal in the Software without ;;; restriction, including without limitation the rights to use, copy, ;;; modify, merge, publish, distribute, sublicense, and/or sell copies ;;; of the Software, and to permit persons to whom the Software is ;;; furnished to do so, subject to the following conditions: ;;; The above copyright notice and this permission notice shall be ;;; included in all copies or substantial portions of the Software. ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;;; DEALINGS IN THE SOFTWARE. ;; The encoder in the cl-json package didn't work the way I needed it ;; to, hence this code which is partially stolen from that package. (in-package :clouchdb) (defparameter *symbol-to-string-fn* #'js::symbol-to-js) (defun lisp-special-char-to-json (lisp-char) (car (rassoc lisp-char *json-lisp-escaped-chars*))) (defun write-json-chars (s stream) (declare (inline lisp-special-char-to-json)) (loop for ch across s for code = (char-code ch) for special = (lisp-special-char-to-json ch) do (cond ((and special (not (char= special #\/))) (write-char #\\ stream) (write-char special stream)) ((<= code #x1f) (format stream "\\u~4,'0x" code)) (t (write-char ch stream))))) (defun write-json-string (s stream) (write-char #\" stream) (if (stringp s) (write-json-chars s stream) (encode-json s stream)) (write-char #\" stream)) (defun write-json-number (nr stream) (if (integerp nr) (format stream "~d" nr) (format stream "~f" nr))) (defmethod write-json-symbol(symbol stream) (cond ((null symbol) (write-json-chars "null" stream)) ((eq 't symbol) (write-json-chars "true" stream)) (t (write-json-string (funcall *symbol-to-string-fn* symbol) stream)))) (defun keyword-assocp (e) "Return true if element is a list that begins with a keyword. This is used to help determine associative list-ness." (and (listp e) (keywordp (car e)))) (defun assoclp (e) "Return true if parameter is, or really looks like, an associative list. Dead giveaways include cons elements in the list that begin with a keyword. Returns the element that produced a positive result, or nil." (labels ((improperlistp (list) (and (listp list) (not (listp (cdr list))))) (test (list) (cond ((null list) nil) ((keyword-assocp (car list)) (car list)) ((improperlistp (car list)) (car list)) ((test (cdr list)))))) (and (listp e) (test e)))) (defun write-alist (d stream) (write-char #\{ stream) (loop for e on d do (let ((cons (car e))) (cond ((stringp (car cons)) (write-string (doublequote (car cons)) stream)) ((symbolp (car cons)) (write-json-symbol (car cons) stream))) (write-char #\: stream) (encode (cdr (car e)) stream)) when (cdr e) do (write-char #\, stream)) (write-char #\} stream)) (defun write-list (d stream) (write-char #\[ stream) (loop for e on d do (encode (car e) stream) when (cdr e) do (write-char #\, stream)) (write-char #\] stream)) (defun encode (d stream) (cond ((null d) (write-string "null" stream)) ((numberp d) (write-json-number d stream)) ((symbolp d) (write-json-symbol d stream)) ((stringp d) (write-json-string d stream)) ((assoclp d) (write-alist d stream)) ((listp d) (write-list d stream)))) (defun encode-document (doc) "Encode document with special support for detecting and handling associative lists." (with-output-to-string (stream) (if (null doc) (write-string "{}" stream) (encode doc stream)))) From peddy at common-lisp.net Thu Dec 20 23:44:42 2007 From: peddy at common-lisp.net (peddy) Date: Thu, 20 Dec 2007 18:44:42 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/public_html Message-ID: <20071220234442.C3C2D3C08B@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/public_html In directory clnet:/tmp/cvs-serv30956/public_html Modified Files: index.html Log Message: Spelling fixes --- /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/19 15:45:00 1.8 +++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/20 23:44:40 1.9 @@ -36,12 +36,13 @@
  • Download and Installation

  • Examples

  • Support and mailing lists

  • -
  • API Reference

  • - +
  • API Reference

    + +
  • Symbol Index

  • Issues and Bugs

  • From peddy at common-lisp.net Thu Dec 20 23:57:10 2007 From: peddy at common-lisp.net (peddy) Date: Thu, 20 Dec 2007 18:57:10 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071220235710.0FF013C08A@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv31681/src Modified Files: encoder.lisp clouchdb.asd Log Message: Added encoder to asd file list, added missing defparmeter to encoding.lisp --- /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/20 23:43:33 1.1 +++ /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/20 23:57:09 1.2 @@ -29,6 +29,16 @@ (defparameter *symbol-to-string-fn* #'js::symbol-to-js) +(defparameter *json-lisp-escaped-chars* + `((#\" . #\") + (#\\ . #\\) + (#\/ . #\/) + (#\b . #\Backspace) + (#\f . ,(code-char 12)) + (#\n . #\Newline) + (#\r . #\Return) + (#\t . #\Tab))) + (defun lisp-special-char-to-json (lisp-char) (car (rassoc lisp-char *json-lisp-escaped-chars*))) --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.asd 2007/12/17 13:58:32 1.2 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.asd 2007/12/20 23:57:09 1.3 @@ -41,5 +41,6 @@ :cl-json :flexi-streams) :components ((:file "package") - (:file "clouchdb"))) + (:file "clouchdb") + (:file "encoder"))) From peddy at common-lisp.net Fri Dec 21 19:58:32 2007 From: peddy at common-lisp.net (peddy) Date: Fri, 21 Dec 2007 14:58:32 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071221195832.2DEBC5003A@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv32651/src Modified Files: tests.lisp package.lisp examples.lisp encoder.lisp clouchdb.lisp changelog.txt Log Message: Field name encoding updates, documentation reflecting those changes --- /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/18 21:33:34 1.6 +++ /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/21 19:58:31 1.7 @@ -125,31 +125,39 @@ (:documentation "Test document-property using property name strings") general-tests-document-property-string (ensure-same "name1" - (document-property "name" '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (document-property "name" '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) (ensure-same "name2" - (document-property "Name" '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (document-property "Name" '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) (ensure-same "name3" - (document-property "NaMe" '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3"))))) + (document-property "NaMe" '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3"))))) (addtest (clouchdb-general-tests) (:documentation "Test document-property using keyword symbols") general-tests-document-property-keyword (ensure-same "name1" - (document-property :NAME '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (document-property :NAME '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) (ensure-same "name2" - (document-property :*NAME '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (document-property :-NAME '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) (ensure-same "name3" - (document-property :*NA-ME'((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3"))))) + (document-property :-NA-ME'((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3"))))) (addtest (clouchdb-general-tests) (:documentation "Test document-property using non-keyword symbols") general-tests-document-property-symbol (ensure-same "name1" - (document-property 'name '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (document-property 'name '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) (ensure-same "name2" - (document-property '*name '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3")))) + (document-property '-name '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) (ensure-same "name3" - (document-property '*na-me '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "name3"))))) + (document-property '-na-me '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3"))))) + +(addtest (clouchdb-general-tests) + (:documentation "Test case-encoded field name functions") + general-tests-case-encoded + (ensure-same "lowercase" (as-field-name-string (as-keyword-symbol "lowercase"))) + (ensure-same "MixedCase" (as-field-name-string (as-keyword-symbol "MixedCase"))) + (ensure-same "Mixed-Case-Hyphen" (as-field-name-string (as-keyword-symbol "Mixed-Case-Hyphen"))) + (ensure-same "UPPER-CASE" (as-field-name-string (as-keyword-symbol "UPPER-CASE")))) ;; ;; Db Administration Tests --- /project/clouchdb/cvsroot/clouchdb/src/package.lisp 2007/12/17 13:58:32 1.2 +++ /project/clouchdb/cvsroot/clouchdb/src/package.lisp 2007/12/21 19:58:32 1.3 @@ -30,6 +30,8 @@ :*host* :*port* :*db-name* + :as-keyword-symbol + :as-field-name-string :db-existential-error :db-does-not-exist :db-already-exists --- /project/clouchdb/cvsroot/clouchdb/src/examples.lisp 2007/12/17 23:22:23 1.4 +++ /project/clouchdb/cvsroot/clouchdb/src/examples.lisp 2007/12/21 19:58:32 1.5 @@ -95,15 +95,15 @@ ;; Strings may be used for field names instead of symbols when ;; submitting documents. Fetched documents will always have ;; symbols for field names regardless of how they were created. - (create-document '(("name" . "Czech Republic") - ("tags" . ("country" "european")) - ("motto" . "Truth prevails") - ("demographics" . ((:population . 10230000) - ;; A nested map property: - (:religion . ((:agnostic . 0.59) - (:roman-catholic . 0.26) - (:protestant . 2.5))) - (:political-system . "democracy")))) + (create-document '((:name . "Czech Republic") + (:tags . ("country" "european")) + (:motto . "Truth prevails") + (:demographics . ((:population . 10230000) + ;; A nested map property: + (:religion . ((:agnostic . 0.59) + (:roman-catholic . 0.26) + (:protestant . 2.5))) + (:political-system . "democracy")))) :id "czechrepublic") ;; Create a persistant view that retrieves documents by their ;; tags. Views can also be queried with a browser at: --- /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/20 23:57:09 1.2 +++ /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/21 19:58:32 1.3 @@ -23,12 +23,10 @@ ;;; DEALINGS IN THE SOFTWARE. ;; The encoder in the cl-json package didn't work the way I needed it -;; to, hence this code which is partially stolen from that package. +;; to, hence this code which is mostly stolen from that package. (in-package :clouchdb) -(defparameter *symbol-to-string-fn* #'js::symbol-to-js) - (defparameter *json-lisp-escaped-chars* `((#\" . #\") (#\\ . #\\) @@ -39,6 +37,98 @@ (#\r . #\Return) (#\t . #\Tab))) +;; (defun val-to-string (val) +;; (if (symbolp val) +;; (string-downcase (symbol-name val)) +;; (princ-to-string val))) + +;; (defun string-split (string separators &key (keep-separators nil) (remove-empty-subseqs nil)) +;; (do ((len (length string)) +;; (i 0 (1+ i)) +;; (last 0) +;; res) +;; ((= i len) +;; (let ((split (if (> i last) +;; (cons (subseq string last i) res) +;; res))) +;; (nreverse (if remove-empty-subseqs +;; (delete "" split :test #'string-equal) +;; split)))) +;; (when (member (char string i) separators) +;; (push (subseq string last i) res) +;; (when keep-separators (push (string (char string i)) res)) +;; (setf last (1+ i))))) + +;; (defparameter *special-chars* +;; '((#\! . "Bang") +;; (#\? . "What") +;; (#\# . "Hash") +;; (#\@ . "At") +;; (#\% . "Percent") +;; (#\+ . "Plus") +;; (#\* . "Star") +;; (#\/ . "Slash"))) + +;;; Parenscript-style symbol -> Javascript-style symbol + +;; (defun constant-string-p (string) +;; (let ((len (length string)) +;; (constant-chars '(#\+ #\*))) +;; (and (> len 2) +;; (member (char string 0) constant-chars) +;; (member (char string (1- len)) constant-chars)))) + +;; (defun first-uppercase-p (string) +;; (and (> (length string) 1) +;; (member (char string 0) '(#\+ #\*)))) + +;; (defun untouchable-string-p (string) +;; (and (> (length string) 1) +;; (char= #\: (char string 0)))) + +;; (defun symbol-to-js (symbol) +;; "Given a Lisp symbol or string, produces to a valid JavaScript +;; identifier by following transformation heuristics case conversion. For +;; example, paren-script becomes parenScript, *some-global* becomes +;; SOMEGLOBAL. (stolen from parenscript)" +;; (when (symbolp symbol) +;; (setf symbol (symbol-name symbol))) +;; (let ((symbols (string-split symbol '(#\. #\[ #\]) :keep-separators t :remove-empty-subseqs t))) +;; (cond ((null symbols) "") +;; ((= (length symbols) 1) +;; (let (res +;; (do-not-touch nil) +;; (lowercase t) +;; (all-uppercase nil)) +;; (cond ((constant-string-p symbol) +;; (setf all-uppercase t +;; symbol (subseq symbol 1 (1- (length symbol))))) +;; ((first-uppercase-p symbol) +;; (setf lowercase nil +;; symbol (subseq symbol 1))) +;; ((untouchable-string-p symbol) +;; (setf do-not-touch t +;; symbol (subseq symbol 1)))) +;; (flet ((reschar (c) +;; (push (cond +;; (do-not-touch c) +;; ((and lowercase (not all-uppercase)) +;; (char-downcase c)) +;; (t (char-upcase c))) +;; res) +;; (setf lowercase t))) +;; (dotimes (i (length symbol)) +;; (let ((c (char symbol i))) +;; (cond +;; ((eql c #\-) +;; (setf lowercase (not lowercase))) +;; ((assoc c *special-chars*) +;; (dolist (i (coerce (cdr (assoc c *special-chars*)) 'list)) +;; (reschar i))) +;; (t (reschar c)))))) +;; (coerce (nreverse res) 'string))) +;; (t (string-join (mapcar #'symbol-to-js symbols) ""))))) + (defun lisp-special-char-to-json (lisp-char) (car (rassoc lisp-char *json-lisp-escaped-chars*))) @@ -72,7 +162,10 @@ (cond ((null symbol) (write-json-chars "null" stream)) ((eq 't symbol) (write-json-chars "true" stream)) - (t (write-json-string (funcall *symbol-to-string-fn* symbol) stream)))) + (t (write-json-string (as-field-name-string symbol) stream)))) + +;; (t (write-json-string (symbol-to-js symbol) stream)))) +;; (t (write-json-string (funcall *symbol-to-string-fn* symbol) stream)))) (defun keyword-assocp (e) "Return true if element is a list that begins with a keyword. This --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/20 23:40:29 1.12 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/21 19:58:32 1.13 @@ -223,10 +223,6 @@ associative list or hashtable, to json data" (encode-document doc)) -;; (cond ((listp doc) -;; (json:encode-json-alist-to-string doc)) -;; (t -;; (json:encode-json-to-string doc)))) (defun document-as-hash (doc) "Convert a document to a hashtable if it isn't one already. Document @@ -247,35 +243,51 @@ new-doc)) (t doc))) - (defun camel-case-to-lisp (string) "Converts a string in camelCase to the same lisp-friendly syntax used in parenscript. -Stolen from the cl-json library since it's not exported. Examples: -\"camelCase\" -> \"CAMEL-CASE\", \"CamelCase\" -> \"*CAMEL-CASE\", -\"dojo.widget.TreeNode\" -> \"DOJO.WIDGET.*TREE-NODE\"" +Stolen from the cl-json library since it's not exported from +there. Examples: \"camelCase\" -> \"CAMEL-CASE\", \"CamelCase\" -> +\"*CAMEL-CASE\", \"dojo.widget.TreeNode\" -> +\"DOJO.WIDGET.*TREE-NODE\"" + (with-output-to-string (out) + (loop for ch across string + do + (when (upper-case-p ch) + (write-char #\- out)) + (write-char (char-upcase ch) out)))) + +(defun lisp-to-camel-case (string) + "Reverses the output of camel-case-to-lisp" (with-output-to-string (out) (loop for ch across string - with last-char do - (if (upper-case-p ch) - (progn - (if (and last-char (lower-case-p last-char)) - (write-char #\- out) - (write-char #\* out)) - (write-char ch out)) - (write-char (char-upcase ch) out)) - (setf last-char ch)))) + with last-char do + (cond ((char= #\- ch) + (if (and last-char (char= #\- last-char)) + (write-char #\- out))) + (t (write-char (if (and last-char (char= #\- last-char)) + (char-upcase ch) + (char-downcase ch)) + out))) + (setf last-char ch)))) (defun as-keyword-symbol (value) "Return value in a form that would be used to identify the car of a value in a document. For example, a value of \"FIELD-NAME\" would return :FIELD-NAME, 'FIELD-NAME would become :FIELD-NAME, and \"Field-Name\" would become \":*FIELD-NAME\"." - (cond ((keywordp value) - value) - ((stringp value) + (cond ((stringp value) (intern (camel-case-to-lisp value) "KEYWORD")) + ((keywordp value) + value) ((symbolp value) - (as-keyword-symbol (intern (symbol-name value) "KEYWORD"))))) + (as-keyword-symbol (intern (symbol-name value) "KEYWORD"))) + (t value))) + +(defun as-field-name-string (value) + "Convert a case-encoded symbol to a potentially mixed case string." + (cond ((symbolp value) + (lisp-to-camel-case (symbol-name value))) + (t value))) (defun document-property (name doc) "Get the value associated with the document property or nil if there @@ -302,9 +314,10 @@ (apply #'drakma:http-request (make-uri uri) keys) ;; (format t " -> uri: ~S~%" uri) ;; (format t " -> headers: ~S~%" headers) - (if must-close - (json:decode-json-from-string body) - nil)))) + (cond (must-close +;; (format t "body: ~S~%" body) + (json:decode-json-from-string body)) + (t nil))))) ;; ;; --- /project/clouchdb/cvsroot/clouchdb/src/changelog.txt 2007/12/18 18:20:09 1.5 +++ /project/clouchdb/cvsroot/clouchdb/src/changelog.txt 2007/12/21 19:58:32 1.6 @@ -1,4 +1,11 @@ +0.0.7: + - Copied and modified cl-json encoding functions to better handle + conversion of lisp associative lists to json data types. + - Updated documentation to recommend using only keyword symbols for + field names. + - Export + 0.0.6: - Finally fixed utf-8 encoding bug for document contents - Revised document ID encoding to 1) Support utf-8 characters From peddy at common-lisp.net Fri Dec 21 19:58:32 2007 From: peddy at common-lisp.net (peddy) Date: Fri, 21 Dec 2007 14:58:32 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/public_html Message-ID: <20071221195832.63A46830A0@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/public_html In directory clnet:/tmp/cvs-serv32651/public_html Modified Files: index.html Log Message: Field name encoding updates, documentation reflecting those changes --- /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/20 23:44:40 1.9 +++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/21 19:58:32 1.10 @@ -93,7 +93,7 @@
  • Parenscript
  • LIFT testing framework
  • An available CouchDb server, - current minimum supported version is 0.7, now tested on 7.2
  • + minimum supported version is 0.7, now tested on 7.3a

    ASDF Install

    @@ -502,10 +502,10 @@

    -Executes the contained statements in the context of any of the -specified connection values. Sets the host name, database name, -protocol ("http" or "https") or port number of the CouchDb server to -use in the expressions in the body. +Executes the statements in body in the context of the specified +connection values. Sets the host name, database name, protocol ("http" +or "https") or port number of the CouchDb server to use in the +expressions in the body.

    Example: @@ -544,32 +544,33 @@

    Document content takes the form of an associative list. The car of - each element of the associative list may be either a string or a - symbol. For example, each of the following calls to - (create-document) creates a document with a field which will be - named "name" in the database, but which will be :NAME (a keyword - symbol) when the documents are retrieved: + each associative list element represents the document field name, + the cdr contains the value. Field names are specified as keyword + symbols. The following example demonstrates the creation of a + simple document with two fields, :name and :agent, and with a + specified document ID:

    -(create-document '((:name . "Max")) :id "keyword")
    -(get-document "keyword")
    -=> ((:_ID . "keyword") (:_REV . "3674093994") (:NAME . "Max"))
    -
    -(create-document '((name . "Max")) :id "symbol")
    -(get-document "symbol")
    -=> ((:_ID . "symbol") (:_REV . "3189074278") (:NAME . "Max"))
    -
    -(create-document '(("name" . "Max")) :id "string")
    -(get-document "string")
    -=> ((:_ID . "string") (:_REV . "3730286488") (:NAME . "Max"))
    -
    +(create-document '((:name . "Max") (:agent . 86)) :id "agent86") +(get-document "agent86") +=> ((:_ID . "agent86") (:_REV . "3674093994") (:NAME . "Max") (:AGENT . 86)) + + +

    + As described above, clouchdb recognizes keyword symbols as field + names, this helps it distinguish . +

    - Field names in CouchDb are case sensitive. To specify a field name - that uses mixed case, you may either use a string value, or encode - the symbol appropriately. In the following example a single - document is created with two fields named, "name" and "Name": + Field names in CouchDb are case sensitive. Field names specified + with keyword symbols are normally stored as lower case characters + in CouchDb, however it is possible to use mixed case field names + as well. +

    + +

    + To create a mixed case field name value you can use the

    @@ -650,6 +651,67 @@
     

    [Function]
    +as-keyword-symbol string +

    +
    +

    + Create a keyword symbol from a string, encode case information in + the result. This function translates Json field names from CouchDb + into the Lisp keyword symbols used in documents. +

    +

    +Example: +

    +
    +  ;; No case encoding
    +  (as-keyword-symbol "normal")
    +  => :NORMAL
    +
    +  ;; Upper case
    +  (as-keyword-symbol "UPPER-CASE")
    +  => :-U-P-P-E-R--C-A-S-E
    +
    +  ;; Mixed case
    +  (as-keyword-symbol "MixedCase")
    +  =>:-MIXED-CASE
    +
    +

    +See (as-field-name-string) +

    +
    + + +

    [Function]
    +as-field-name-string symbol +

    +
    +

    + Convert a field name keyword symbol to a camelCase style + string. This function produces the field name in the format that + will be used and visible in CouchDb. +

    +

    +Example: +

    +
    +  ;; No case encoding
    +  (as-field-name-string :normal)
    +  => "normal"
    +
    +  ;; Upper case
    +  (as-field-name-string :-u-p-p-e-r--c-a-s-e)
    +  => "UPPER-CASE"
    +
    +  ;; Mixed case
    +  (as-field-name-string :-Mixed-Case)
    +  => "MixedCase"
    +
    +

    + See (as-keyword-symbol) +

    +
    + +

    [Function]
    create-document doc &key id

    @@ -1003,6 +1065,8 @@

    ad-hoc-view
    + as-field-name-string
    + as-keyword-symbol
    create-db
    create-document
    create-view
    @@ -1048,18 +1112,6 @@ support for attachments is not yet implemented in Clouchdb. -

  • Document Encoding Clouchdb currently - uses cl-json - to encode and decode lisp data in Json, the native CouchDb - protocol data format. Unfortunately it is not always possible for - cl-json to discern the correct Json type from a given lisp value, - and this can result in unexpected document structure. For example, - it easily confuses associative list data with array data if the - first element's value in an associative list is a sequence. This - problem is being looked into, suggestions for resolutions to this - problem are welcome. -
  • - From peddy at common-lisp.net Fri Dec 21 20:04:34 2007 From: peddy at common-lisp.net (peddy) Date: Fri, 21 Dec 2007 15:04:34 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071221200434.A2C04830A8@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv2499/src Modified Files: encoder.lisp Log Message: Removed dead code in encoder, updated news in docs --- /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/21 19:58:32 1.3 +++ /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/21 20:04:34 1.4 @@ -37,98 +37,6 @@ (#\r . #\Return) (#\t . #\Tab))) -;; (defun val-to-string (val) -;; (if (symbolp val) -;; (string-downcase (symbol-name val)) -;; (princ-to-string val))) - -;; (defun string-split (string separators &key (keep-separators nil) (remove-empty-subseqs nil)) -;; (do ((len (length string)) -;; (i 0 (1+ i)) -;; (last 0) -;; res) -;; ((= i len) -;; (let ((split (if (> i last) -;; (cons (subseq string last i) res) -;; res))) -;; (nreverse (if remove-empty-subseqs -;; (delete "" split :test #'string-equal) -;; split)))) -;; (when (member (char string i) separators) -;; (push (subseq string last i) res) -;; (when keep-separators (push (string (char string i)) res)) -;; (setf last (1+ i))))) - -;; (defparameter *special-chars* -;; '((#\! . "Bang") -;; (#\? . "What") -;; (#\# . "Hash") -;; (#\@ . "At") -;; (#\% . "Percent") -;; (#\+ . "Plus") -;; (#\* . "Star") -;; (#\/ . "Slash"))) - -;;; Parenscript-style symbol -> Javascript-style symbol - -;; (defun constant-string-p (string) -;; (let ((len (length string)) -;; (constant-chars '(#\+ #\*))) -;; (and (> len 2) -;; (member (char string 0) constant-chars) -;; (member (char string (1- len)) constant-chars)))) - -;; (defun first-uppercase-p (string) -;; (and (> (length string) 1) -;; (member (char string 0) '(#\+ #\*)))) - -;; (defun untouchable-string-p (string) -;; (and (> (length string) 1) -;; (char= #\: (char string 0)))) - -;; (defun symbol-to-js (symbol) -;; "Given a Lisp symbol or string, produces to a valid JavaScript -;; identifier by following transformation heuristics case conversion. For -;; example, paren-script becomes parenScript, *some-global* becomes -;; SOMEGLOBAL. (stolen from parenscript)" -;; (when (symbolp symbol) -;; (setf symbol (symbol-name symbol))) -;; (let ((symbols (string-split symbol '(#\. #\[ #\]) :keep-separators t :remove-empty-subseqs t))) -;; (cond ((null symbols) "") -;; ((= (length symbols) 1) -;; (let (res -;; (do-not-touch nil) -;; (lowercase t) -;; (all-uppercase nil)) -;; (cond ((constant-string-p symbol) -;; (setf all-uppercase t -;; symbol (subseq symbol 1 (1- (length symbol))))) -;; ((first-uppercase-p symbol) -;; (setf lowercase nil -;; symbol (subseq symbol 1))) -;; ((untouchable-string-p symbol) -;; (setf do-not-touch t -;; symbol (subseq symbol 1)))) -;; (flet ((reschar (c) -;; (push (cond -;; (do-not-touch c) -;; ((and lowercase (not all-uppercase)) -;; (char-downcase c)) -;; (t (char-upcase c))) -;; res) -;; (setf lowercase t))) -;; (dotimes (i (length symbol)) -;; (let ((c (char symbol i))) -;; (cond -;; ((eql c #\-) -;; (setf lowercase (not lowercase))) -;; ((assoc c *special-chars*) -;; (dolist (i (coerce (cdr (assoc c *special-chars*)) 'list)) -;; (reschar i))) -;; (t (reschar c)))))) -;; (coerce (nreverse res) 'string))) -;; (t (string-join (mapcar #'symbol-to-js symbols) ""))))) - (defun lisp-special-char-to-json (lisp-char) (car (rassoc lisp-char *json-lisp-escaped-chars*))) From peddy at common-lisp.net Fri Dec 21 20:04:34 2007 From: peddy at common-lisp.net (peddy) Date: Fri, 21 Dec 2007 15:04:34 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/public_html Message-ID: <20071221200434.D93275C000@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/public_html In directory clnet:/tmp/cvs-serv2499/public_html Modified Files: index.html Log Message: Removed dead code in encoder, updated news in docs --- /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/21 19:58:32 1.10 +++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/21 20:04:34 1.11 @@ -50,6 +50,10 @@

    News

      +
    • Dec 20, 2007 Released version 0.0.7 with fixes for what + had been cl-json's inability to distinguish certain database field + value types in the document associative list. +
    • Dec 19, 2007 Released version 0.0.6 with full support for utf-8 character encoding. This change includes support for non-Latin characters in document IDs and in document From peddy at common-lisp.net Sat Dec 22 02:11:14 2007 From: peddy at common-lisp.net (peddy) Date: Fri, 21 Dec 2007 21:11:14 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/public_html Message-ID: <20071222021114.358885C184@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/public_html In directory clnet:/tmp/cvs-serv5626 Modified Files: index.html Log Message: Field name encoding doc changes --- /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/21 20:04:34 1.11 +++ /project/clouchdb/cvsroot/clouchdb/public_html/index.html 2007/12/22 02:11:14 1.12 @@ -562,34 +562,32 @@

    - As described above, clouchdb recognizes keyword symbols as field - names, this helps it distinguish . + By giving keyword symbols their special significance as field + names identifiers, clouchdb is able to distinguish between field + names and field values in certain situations which would otherwise + be ambiguous. For example, keyword symbols allow clocuhdb to + distinguish between associative lists and lists that contain + other, non-associative lists.

    Field names in CouchDb are case sensitive. Field names specified - with keyword symbols are normally stored as lower case characters - in CouchDb, however it is possible to use mixed case field names - as well. -

    - -

    - To create a mixed case field name value you can use the + with keyword symbols are normally converted to or from lower case + in communication with the CouchDb server, however it is possible + to specify case in field names like so:

    -(create-document '(("FieldName" . "Value")) :id "string-mixed")
    -(get-document "string-mixed")
    -=> ((:_ID . "string-mixed") (:_REV . "3753754378") (:*FIELD-NAME . "Value"))
    -
    -(create-document '((:*field-name . "Value")) :id "keyword-mixed")
    -(get-document "keyword-mixed")
    -=> ((:_ID . "keyword-mixed") (:_REV . "2016717365") (:*FIELD-NAME . "Value"))
    -
    -(create-document '((*field-name . "Value")) :id "symbol-mixed")
    -(get-document "symbol-mixed")
    -=> ((:_ID . "symbol-mixed") (:_REV . "4010145031") (:*FIELD-NAME . "Value"))
    +;; Create a document with a mixed case field name. This document
    +;; will appear in the database as, "fieldName"
    +(create-document '((:field-name . "Value")) :id "mixed-case1")
    +(get-document "mixed-case1")
    +=> ((:_ID . "mixed-case1") (:_REV . "2016717365") (:*FIELD-NAME . "Value"))
       
    + +

    + But see Issues and Bugs +

    The native document representation in the CouchDb protocol is @@ -609,21 +607,12 @@ (create-document '((:string . "String Value") (:number . 42.0) (:list . (milk eggs "green beans")) - (:alist . ((:size . 3) - (:string . "Another String") + (:alist . ((:string . "Another String") + (:size . 3) (:list . ("un" "deux" "trois")) (:another-alist . ((a . "A") (b . "B"))))))) -

    - Note: There are issues related to the construction of - documents with non-trivial structure and their translation into - corresponding Json data, specifically with regard to confusion - between associative lists and - sequences. See Issues and Bugs for - more information. -

    -

    @@ -681,6 +670,7 @@

    See (as-field-name-string) + and Issues and Bugs

    @@ -712,6 +702,7 @@

    See (as-keyword-symbol) + and Issues and Bugs

    @@ -1116,6 +1107,11 @@ support for attachments is not yet implemented in Clouchdb. +
  • Mixed case field name encoding Parenscript, cl-json, and + clouchdb don't always agree on how mixed-case field names should be + encoded and decoded. This will be addressed in a future release, in + the mean time, it's best to avoid this feature if possible. +
  • From peddy at common-lisp.net Fri Dec 28 12:43:15 2007 From: peddy at common-lisp.net (peddy) Date: Fri, 28 Dec 2007 07:43:15 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071228124315.B0B58340BE@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv5053/src Modified Files: clouchdb.lisp Log Message: Added :if-exists options to various functions --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/21 19:58:32 1.13 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/28 12:43:15 1.14 @@ -449,7 +449,7 @@ ;; ?descending=false causes error ATM `(("descending" . "true")))))) -(defun get-document (id &key revision revisions revision-info) +(defun get-document (id &key revision revisions revision-info (if-missing nil if-missing-p)) "Get a document by ID. Returns nil if the document does not exist. The revision property specifies an optional revision number, if unspecified, the most recent revision is returned. The revisions and @@ -472,7 +472,12 @@ :method :get :parameters parameters)))) (if (document-property :error res) - (error 'document-missing :id id) + (progn + (cond ((eq if-missing :ignore) + nil) + ((and if-missing-p (not (eq if-missing :error))) + if-missing) + (t (error 'document-missing :id id)))) res)))) (defun put-document (doc &key id) @@ -545,7 +550,7 @@ (string-join (mapcar #'document-to-json docs)) " ] ")))) -(defun delete-document (&key document id revision) +(defun delete-document (&key document id revision if-missing) "Delete a document. By default delete the current revision of the document. If specified, the document parameter must include the CouchDb special variables :_id and :_rev. If the id is speicified but @@ -557,11 +562,16 @@ :method :delete))) (cond ((not (null document)) (delete-document :id (document-property :_id document) - :revision (document-property :_rev document))) + :revision (document-property :_rev document) + :if-missing if-missing)) ((not id) (error 'id-missing)) ((not revision) - (del id (document-property :_rev (get-document id)))) + (let ((doc (get-document id :if-missing (if (eq if-missing :ignore) + :ignore + :error)))) + (when doc + (del id (document-property :_rev doc))))) (t (del id revision))))) ;; ;; Views API @@ -601,10 +611,11 @@ (cat "{\"language\" : \"text/javascript\"," "\"views\" : {" (mk-view-js view-defs) "}}"))))) -(defun delete-view (id &key revision) +(defun delete-view (id &key revision if-missing) "Delete identified view document" (ensure-db () - (delete-document :id (cat "_design/" (url-encode id)) :revision revision))) + (delete-document :id (cat "_design/" (url-encode id)) + :revision revision :if-missing if-missing))) (defun invoke-view (id view &rest options &key key start-key start-key-docid end-key count update descending skip) From peddy at common-lisp.net Fri Dec 28 16:25:51 2007 From: peddy at common-lisp.net (peddy) Date: Fri, 28 Dec 2007 11:25:51 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071228162551.56C3217046@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv25209 Modified Files: tests.lisp package.lisp examples.lisp encoder.lisp clouchdb.lisp clouchdb.asd Log Message: Switched to simpler keyword encoding scheme --- /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/21 19:58:31 1.7 +++ /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/28 16:25:51 1.8 @@ -68,7 +68,7 @@ (let ((docs)) (dolist (e data) (let ((doc (get-document (document-property id-field e)))) - (if (not (document-property :_id doc)) + (if (not (document-property :|_id| doc)) (error (format t "Document ID=~S not found" (document-property id-field e)))) (push e docs))) @@ -114,7 +114,7 @@ (:documentation "Test document-property") general-tests-document-property (ensure - (let ((doc '((:NAME . "name1") (:*NAME . "name2") (:*NA-ME . "NaMe")))) + (let ((doc '((:NAME . "name1") (:|Name| . "name2") (:|NaMe| . "NaMe")))) (reduce #'(lambda (a b) (and a b)) (mapcar #'(lambda (e) (equal (cdr (assoc (car e) doc)) @@ -171,24 +171,24 @@ (addtest (clouchdb-db-admin-tests) (:documentation "Look for the welcome message and version info from server") generic-server-info-query - (ensure-same "Welcome" (document-property :couchdb (get-db-info))) - (ensure (document-property :version (get-db-info)))) + (ensure-same "Welcome" (document-property :|couchdb| (get-db-info))) + (ensure (document-property :|version| (get-db-info)))) (addtest (clouchdb-db-admin-tests) (:documentation "Ensure get-db-info reports non-existant databases") db-non-existance-test (ensure (setf *db-name* (create-temp-db-name))) - (ensure-same "not_found" (document-property :error (get-db-info))) - (ensure-same "missing" (document-property :reason (get-db-info)))) + (ensure-same "not_found" (document-property :|error| (get-db-info))) + (ensure-same "missing" (document-property :|reason| (get-db-info)))) (addtest (clouchdb-db-admin-tests) (:documentation "Create a database and ensure it's there, ensure it's deleted too") db-creation-test (ensure (setf *db-name* (create-temp-db))) - (ensure-same (document-property :db_name (get-db-info)) *db-name*) - (ensure-same 0 (document-property :doc_count (get-db-info))) - (ensure-same 0 (document-property :update_seq (get-db-info))) - (ensure (document-property :ok (delete-db)))) + (ensure-same (document-property :|db_name| (get-db-info)) *db-name*) + (ensure-same 0 (document-property :|doc_count| (get-db-info))) + (ensure-same 0 (document-property :|update_seq| (get-db-info))) + (ensure (document-property :|ok| (delete-db)))) (addtest (clouchdb-db-admin-tests) (:documentation "Make sure deleting a nonexistant db generates an error") @@ -200,7 +200,7 @@ db-ignore-delete-non-existant-db (ensure (document-property - :error + :|error| (delete-db :if-missing :ignore :db-name (create-temp-db-name))))) (addtest (clouchdb-db-admin-tests) @@ -214,20 +214,20 @@ (:documentation "Ignore the duplicate db create error") db-ignore-create-existant-db (ensure (setf *db-name* (create-temp-db))) - (ensure (document-property :ok (create-db :if-exists :ignore))) + (ensure (document-property :|ok| (create-db :if-exists :ignore))) (ensure (delete-db))) (addtest (clouchdb-db-admin-tests) (:documentation "recreate option for create-db on existing db") db-recreate-db (ensure (setf *db-name* (create-temp-db))) - (ensure (document-property :ok (create-db :if-exists :recreate))) + (ensure (document-property :|ok| (create-db :if-exists :recreate))) (ensure (delete-db))) (addtest (clouchdb-db-admin-tests) (:documentation "recreate option for create-db on non-existant db") db-recreate-nonexistant-db - (ensure (document-property :ok (create-db :if-exists :recreate))) + (ensure (document-property :|ok| (create-db :if-exists :recreate))) (ensure (delete-db))) ;; @@ -248,29 +248,29 @@ (addtest (clouchdb-doc-api-tests) (:documentation "Ensures the temporary db for these tests is succesfully created.") empty-test - (ensure-same (document-property :db_name (get-db-info)) *db-name*)) + (ensure-same (document-property :|db_name| (get-db-info)) *db-name*)) (addtest (clouchdb-doc-api-tests) (:documentation "Create a document with create-document") create-document-auto-id - (ensure (document-property :ok (create-document '((:a "test")))))) + (ensure (document-property :|ok| (create-document '((:a "test")))))) (addtest (clouchdb-doc-api-tests) (:documentation "Create document with create-document, specify document ID") create-document-specified-id - (ensure (document-property :ok (create-document '((:a "test")) :id "specified")))) + (ensure (document-property :|ok| (create-document '((:a "test")) :id "specified")))) (addtest (clouchdb-doc-api-tests) (:documentation "Create a document with a duplicate ID") create-document-specified-id-conflict - (ensure (document-property :ok (create-document '((:a "test")) :id "specified"))) + (ensure (document-property :|ok| (create-document '((:a "test")) :id "specified"))) (ensure-condition 'id-or-revision-conflict (create-document '((:a "test")) :id "specified"))) (addtest (clouchdb-doc-api-tests) (:documentation "Create a document with put-document") put-document-create - (ensure (document-property :ok (put-document '((:a "test")) :id "specified")))) + (ensure (document-property :|ok| (put-document '((:a "test")) :id "specified")))) (addtest (clouchdb-doc-api-tests) (:documentation "Create a document with put-document with no ID (error)") @@ -295,13 +295,13 @@ (put-document doc))) ;; (let ((docinf (get-document "revizedalot" :revision-info t))) - (length (document-property :_revs_info docinf)))))) + (length (document-property :|_revs_info| docinf)))))) (addtest (clouchdb-doc-api-tests) (:documentation "Delete a document by ID") delete-document-by-id - (ensure (document-property :ok (create-document '((:a "test")) :id "specified"))) - (ensure (document-property :ok (delete-document :id "specified")))) + (ensure (document-property :|ok| (create-document '((:a "test")) :id "specified"))) + (ensure (document-property :|ok| (delete-document :id "specified")))) (addtest (clouchdb-doc-api-tests) (:documentation "Delete a document by ID and revision") @@ -309,15 +309,15 @@ (ensure (progn (create-document '((:a . "document")) :id "specified") (let ((doc (get-document "specified"))) - (document-property :ok (delete-document :id (document-property :_id doc) - :revision (document-property :_rev doc))))))) + (document-property :|ok| (delete-document :id (document-property :|_id| doc) + :revision (document-property :|_rev| doc))))))) (addtest (clouchdb-doc-api-tests) (:documentation "Delete a document by document") delete-document-by-document (ensure (progn (create-document '((:a . "document")) :id "polly") - (document-property :ok + (document-property :|ok| (delete-document :document (get-document "polly")))))) (addtest (clouchdb-doc-api-tests) @@ -335,8 +335,8 @@ (addtest (clouchdb-doc-api-tests) (:documentation "Test get-document for existing document.") get-document-test - (ensure (document-property :ok (create-document '((:a . "test")) :id "test"))) - (ensure-same (document-property :_id (get-document "test")) "test")) + (ensure (document-property :|ok| (create-document '((:a . "test")) :id "test"))) + (ensure-same (document-property :|_id| (get-document "test")) "test")) (addtest (clouchdb-doc-api-tests) (:documentation "Update a document property") @@ -353,7 +353,7 @@ add-document-property-test1 (ensure (create-test-documents *people* :id-field :name)) (ensure - (document-property :ok (put-document (cons '(:handsome . "false") + (document-property :|ok| (put-document (cons '(:handsome . "false") (get-document "peter")))))) (addtest (clouchdb-doc-api-tests) @@ -361,25 +361,25 @@ bulk-update-1 (ensure (create-test-documents *people* :id-field :name)) (ensure (let ((docs)) - (dolist (di (document-property :rows (get-all-documents))) + (dolist (di (document-property :|rows| (get-all-documents))) (push (cons '(:new-field . "New Value") - (get-document (document-property :id di))) + (get-document (document-property :|id| di))) docs)) (bulk-document-update docs) (block test - (loop for di in (document-property :rows (get-all-documents)) do + (loop for di in (document-property :|rows| (get-all-documents)) do (if (not (equal "New Value" (document-property :new-field (get-document - (document-property :id di))))) + (document-property :|id| di))))) (return-from test nil))) t)))) (addtest (clouchdb-doc-api-tests) (:documentation "Test document ID encoding") encode-document-id - (ensure (document-property :ok (create-document '((:a "test")) :id "http://google.com"))) - (ensure-same (document-property :_id (get-document "http://google.com")) "http://google.com")) + (ensure (document-property :|ok| (create-document '((:a "test")) :id "http://google.com"))) + (ensure-same (document-property :|_id| (get-document "http://google.com")) "http://google.com")) (addtest (clouchdb-doc-api-tests) (:documentation "Test encoding and decoding of utf-8 document IDs") @@ -388,8 +388,8 @@ (let ((ids '("??ngstr??m Caf??" "????????????????" "?????????????????????????????????"))) (reduce #'(lambda (a b) (and a b)) (mapcar #'(lambda (id) - (and (document-property :ok (create-document nil :id id)) - (equal id (document-property :_id (get-document id))))) + (and (document-property :|ok| (create-document nil :id id)) + (equal id (document-property :|_id| (get-document id))))) ids))))) (addtest (clouchdb-doc-api-tests) @@ -424,7 +424,7 @@ (:korean . "?????? ????????? ?????? ??? ?????????. ????????? ????????? ?????????") (:euro-symbol . "???") (:georgian . "??????????????? ???????????? ?????? ????????? ??????????????????.")))) - (and (document-property :ok (create-document glass-eaters :id "glass-eaters")) + (and (document-property :|ok| (create-document glass-eaters :id "glass-eaters")) (let ((doc (get-document "glass-eaters"))) (reduce #'(lambda (a b) (and a b)) (mapcar #'(lambda (e) @@ -443,17 +443,18 @@ ad-hoc-view-result (ensure-same (length (contains-property *people* :name :pval "marc")) (document-property - :total_rows + :|total_rows| (ad-hoc-view (ps (lambda (doc) - (if (= doc.name "marc") - (map null doc.name)))))))) + (with-slots (*NAME*) doc + (if (= *NAME* "marc") + (map null *NAME*))))))))) (addtest (clouchdb-view-tests) (:documentation "Create an ad-hock view that should return no results") ad-hoc-view-no-result (ensure-same 0 (document-property - :total_rows + :|total_rows| (ad-hoc-view (ps (lambda (doc) (if (= doc.name "marie") @@ -463,12 +464,12 @@ (:documentation "Ensure a view can be created") create-view-test1 (ensure - (document-property :ok + (document-property :|ok| (create-view "friend" (cons "marie-view" (ps (lambda (doc) - (with-slots (friends) doc - (dolist (friend friends) + (with-slots (*friends*) doc + (dolist (friend *friends*) (if (= friend "marie") (map null doc))))))))))) @@ -477,7 +478,7 @@ create-view-test2 (ensure (document-property - :ok + :|ok| (create-view "friend" (cons "marie-view" (ps (lambda (doc) @@ -485,29 +486,29 @@ (dolist (friend friends) (if (= friend "marie") (map null doc)))))))))) - (ensure (document-property :ok (delete-view "friend")))) + (ensure (document-property :|ok| (delete-view "friend")))) (addtest (clouchdb-view-tests) (:documentation "Creating a view that already exists should report an error") create-view-test3 (ensure (document-property - :ok + :|ok| (create-view "friend" (cons "marie-view" (ps (lambda (doc) - (with-slots (friends) doc - (dolist (friend friends) + (with-slots (*friends*) doc + (dolist (friend *friends*) (if (= friend "marie") (map null doc)))))))))) (ensure-same "conflict" (document-property - :error + :|error| (create-view "friend" (cons "marie-view" (ps (lambda (doc) - (with-slots (friends) doc - (dolist (friend friends) + (with-slots (*friends*) doc + (dolist (friend *friends*) (if (= friend "marie") (map null doc))))))))))) @@ -515,30 +516,30 @@ (:documentation "Create a view and see if it can be queried") create-view-query-test1 (ensure - (document-property :ok + (document-property :|ok| (create-view "friend" (cons "marie-view" (ps (lambda (doc) - (with-slots (friends) doc - (dolist (friend friends) + (with-slots (*friends*) doc + (dolist (friend *friends*) (if (= friend "marie") (map null doc)))))))))) - (ensure-same (document-property :total_rows (invoke-view "friend" "marie-view")) + (ensure-same (document-property :|total_rows| (invoke-view "friend" "marie-view")) (length (contains-property *people* :friends :pval "marie")))) (addtest (clouchdb-view-tests) (:documentation "Create a view and see if it can be queried with a key") create-view-keyquery-test1 (ensure - (document-property :ok + (document-property :|ok| (create-view "friend" (cons "fname" (ps (lambda (doc) - (with-slots (friends) doc - (dolist (friend friends) + (with-slots (*friends*) doc + (dolist (friend *friends*) (map friend doc))))))))) (ensure-same (length (document-property - :rows (invoke-view "friend" "fname" :key "claire"))) + :|rows| (invoke-view "friend" "fname" :key "claire"))) (length (contains-property *people* :friends :pval "claire")))) ;; --- /project/clouchdb/cvsroot/clouchdb/src/package.lisp 2007/12/21 19:58:32 1.3 +++ /project/clouchdb/cvsroot/clouchdb/src/package.lisp 2007/12/28 16:25:51 1.4 @@ -25,7 +25,7 @@ (cl:in-package :cl-user) (defpackage :clouchdb - (:use :cl :drakma :json :flexi-streams) + (:use :cl :drakma :flexi-streams) (:export :*scheme* :*host* :*port* --- /project/clouchdb/cvsroot/clouchdb/src/examples.lisp 2007/12/21 19:58:32 1.5 +++ /project/clouchdb/cvsroot/clouchdb/src/examples.lisp 2007/12/28 16:25:51 1.6 @@ -82,28 +82,28 @@ ;; A simple example document using an auto-generated ID supplied ;; by CouchDb: (create-document '(;; Field with associated scalar value: - (:name . "wine") + (:|name| . "wine") ;; Field with array value: - (:tags . ("beverage" "fun" "alcoholic")))) + (:|tags| . ("beverage" "fun" "alcoholic")))) ;; A document with somewhat different structure, and that uses a ;; specified ID (does not use auto-generated ID): - (create-document '((:first-name . "Claude") - (:last-name . "Debussy") - (:tags . ("composer" "french" "impressionist" "european"))) + (create-document '((:|first-name| . "Claude") + (:|last-name| . "Debussy") + (:|tags| . ("composer" "french" "impressionist" "european"))) ;; Specify an ID for this document: :id "cdebussy") ;; Strings may be used for field names instead of symbols when ;; submitting documents. Fetched documents will always have ;; symbols for field names regardless of how they were created. - (create-document '((:name . "Czech Republic") - (:tags . ("country" "european")) - (:motto . "Truth prevails") - (:demographics . ((:population . 10230000) + (create-document '((:|name| . "Czech Republic") + (:|tags| . ("country" "european")) + (:|motto| . "Truth prevails") + (:|demographics| . ((:|population| . 10230000) ;; A nested map property: - (:religion . ((:agnostic . 0.59) - (:roman-catholic . 0.26) - (:protestant . 2.5))) - (:political-system . "democracy")))) + (:|religion| . ((:|Agnostic| . 0.59) + (:|Roman Catholic| . 0.26) + (:|Protestant| . 2.5))) + (:|Political System| . "democracy")))) :id "czechrepublic") ;; Create a persistant view that retrieves documents by their ;; tags. Views can also be queried with a browser at: @@ -122,8 +122,8 @@ (map tag doc))))))) ;; Query the view defined above and print the results (let ((result (invoke-view "tags" "tag" :key "european"))) - (format t "found: ~S documents:~%" (length (document-property :rows result))) - (dolist (doc (document-property :rows result)) + (format t "found: ~S documents:~%" (length (document-property :|rows| result))) + (dolist (doc (document-property :|rows| result)) (format t "---~%ID: ~S~%document:~%~S~%" (document-property :id doc) doc))))) --- /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/21 20:04:34 1.4 +++ /project/clouchdb/cvsroot/clouchdb/src/encoder.lisp 2007/12/28 16:25:51 1.5 @@ -66,15 +66,12 @@ (format stream "~d" nr) (format stream "~f" nr))) -(defmethod write-json-symbol(symbol stream) +(defun write-json-symbol(symbol stream) (cond ((null symbol) (write-json-chars "null" stream)) ((eq 't symbol) (write-json-chars "true" stream)) (t (write-json-string (as-field-name-string symbol) stream)))) -;; (t (write-json-string (symbol-to-js symbol) stream)))) -;; (t (write-json-string (funcall *symbol-to-string-fn* symbol) stream)))) - (defun keyword-assocp (e) "Return true if element is a list that begins with a keyword. This is used to help determine associative list-ness." --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/28 12:43:15 1.14 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/28 16:25:51 1.15 @@ -210,9 +210,9 @@ between an error due to a missing document and a missing database." (let ((result (gensym))) `(let ((,result (progn , at body))) - (when (equal "not_found" (document-property :error ,result)) + (when (equal "not_found" (document-property :|error| ,result)) (let ((dbn (if ,db-name-p ,db-name *db-name*))) - (if (document-property :error (get-db-info :db-name dbn)) + (if (document-property :|error| (get-db-info :db-name dbn)) (error 'db-does-not-exist :result ,result :db-name dbn :uri (make-uri dbn))))) @@ -243,40 +243,11 @@ new-doc)) (t doc))) -(defun camel-case-to-lisp (string) - "Converts a string in camelCase to the same lisp-friendly syntax used in parenscript. -Stolen from the cl-json library since it's not exported from -there. Examples: \"camelCase\" -> \"CAMEL-CASE\", \"CamelCase\" -> -\"*CAMEL-CASE\", \"dojo.widget.TreeNode\" -> -\"DOJO.WIDGET.*TREE-NODE\"" - (with-output-to-string (out) - (loop for ch across string - do - (when (upper-case-p ch) - (write-char #\- out)) - (write-char (char-upcase ch) out)))) - -(defun lisp-to-camel-case (string) - "Reverses the output of camel-case-to-lisp" - (with-output-to-string (out) - (loop for ch across string - with last-char do - (cond ((char= #\- ch) - (if (and last-char (char= #\- last-char)) - (write-char #\- out))) - (t (write-char (if (and last-char (char= #\- last-char)) - (char-upcase ch) - (char-downcase ch)) - out))) - (setf last-char ch)))) - (defun as-keyword-symbol (value) "Return value in a form that would be used to identify the car of a -value in a document. For example, a value of \"FIELD-NAME\" would -return :FIELD-NAME, 'FIELD-NAME would become :FIELD-NAME, and -\"Field-Name\" would become \":*FIELD-NAME\"." +value in a document. For example, a value" (cond ((stringp value) - (intern (camel-case-to-lisp value) "KEYWORD")) + (intern value "KEYWORD")) ((keywordp value) value) ((symbolp value) @@ -286,7 +257,7 @@ (defun as-field-name-string (value) "Convert a case-encoded symbol to a potentially mixed case string." (cond ((symbolp value) - (lisp-to-camel-case (symbol-name value))) + (symbol-name value)) (t value))) (defun document-property (name doc) @@ -316,7 +287,7 @@ ;; (format t " -> headers: ~S~%" headers) (cond (must-close ;; (format t "body: ~S~%" body) - (json:decode-json-from-string body)) + (decode-json-from-string body)) (t nil))))) ;; @@ -371,9 +342,9 @@ (let* ((name (if db-name-p db-name *db-name*)) (res (db-request (cat (url-encode name) "/") :method :put))) - (if (equal "database_already_exists" (document-property :error res)) + (if (equal "database_already_exists" (document-property :|error| res)) (ecase if-exists - ((:ignore) (list (cons :ok t) (cons :ignored t))) + ((:ignore) (list (cons :|ok| t) (cons :|ignored| t))) ((:recreate) (delete-db :db-name name) (create-db :db-name name)) @@ -392,7 +363,7 @@ if-missing parameter." (let* ((name (if db-name-p db-name *db-name*)) (res (db-request (cat (url-encode name) "/") :method :delete))) - (if (and (document-property :error res) (not (eq :ignore if-missing))) + (if (and (document-property :|error| res) (not (eq :ignore if-missing))) (restart-case (error 'db-does-not-exist :result res :db-name name @@ -418,9 +389,9 @@ "Create a temporary database." (let ((db-name (funcall db-name-creator))) (let ((res (create-db :db-name db-name))) - (if (document-property :error res) + (if (document-property :|error| res) (error (format t "Error ~S creating database: ~A" - (document-property :error res) db-name)))) + (document-property :|error| res) db-name)))) db-name)) (defmacro with-temp-db (&body body) @@ -471,7 +442,7 @@ (url-encode id)) :method :get :parameters parameters)))) - (if (document-property :error res) + (if (document-property :|error| res) (progn (cond ((eq if-missing :ignore) nil) @@ -488,7 +459,7 @@ differs from the existing :_id value, then a new document is created with the new ID and the non-special properties of the specified document, since the latter would generate a CouchDb error." - (let ((current-id (document-property :_id doc))) + (let ((current-id (document-property :|_id| doc))) (cond ((not (or current-id id)) (error 'id-missing)) ;; If an ID was specified and that ID does not match the @@ -507,11 +478,11 @@ :content-length nil :content (document-to-json doc) :method :put)))) - (when (document-property :error res) - (error (if (equal "conflict" (document-property :error res)) + (when (document-property :|error| res) + (error (if (equal "conflict" (document-property :|error| res)) 'id-or-revision-conflict 'doc-error) :id (if id id current-id) - :reason (document-property :reason res))) + :reason (document-property :|reason| res))) res))) (defun post-document (doc) @@ -524,8 +495,8 @@ :content-length nil :content (document-to-json doc) :method :post)))) - (when (document-property :error res) - (error 'doc-error) :id nil :reason (document-property :reason res)) + (when (document-property :|error| res) + (error 'doc-error) :id nil :reason (document-property :|reason| res)) res)) (defun create-document (doc &key id) @@ -561,8 +532,8 @@ (url-encode (value-as-string rev))) :method :delete))) (cond ((not (null document)) - (delete-document :id (document-property :_id document) - :revision (document-property :_rev document) + (delete-document :id (document-property :|_id| document) + :revision (document-property :|_rev| document) :if-missing if-missing)) ((not id) (error 'id-missing)) @@ -571,7 +542,7 @@ :ignore :error)))) (when doc - (del id (document-property :_rev doc))))) + (del id (document-property :|_rev| doc))))) (t (del id revision))))) ;; ;; Views API --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.asd 2007/12/20 23:57:09 1.3 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.asd 2007/12/28 16:25:51 1.4 @@ -38,9 +38,10 @@ :serial t :version #.*clouchdb-version* :depends-on (:drakma - :cl-json + :parenscript :flexi-streams) :components ((:file "package") (:file "clouchdb") + (:file "decoder") (:file "encoder"))) From peddy at common-lisp.net Fri Dec 28 16:30:08 2007 From: peddy at common-lisp.net (peddy) Date: Fri, 28 Dec 2007 11:30:08 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071228163008.98DED17046@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv25933 Added Files: decoder.lisp Log Message: Added decoder copied from cl-json --- /project/clouchdb/cvsroot/clouchdb/src/decoder.lisp 2007/12/28 16:30:08 NONE +++ /project/clouchdb/cvsroot/clouchdb/src/decoder.lisp 2007/12/28 16:30:08 1.1 ;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CLOUCHDB; Base: 10 -*- ;;; Copyright (c) 2007 Peter Eddy. All rights reserved. ;;; Permission is hereby granted, free of charge, to any person ;;; obtaining a copy of this software and associated documentation ;;; files (the "Software"), to deal in the Software without ;;; restriction, including without limitation the rights to use, copy, ;;; modify, merge, publish, distribute, sublicense, and/or sell copies ;;; of the Software, and to permit persons to whom the Software is ;;; furnished to do so, subject to the following conditions: ;;; The above copyright notice and this permission notice shall be ;;; included in all copies or substantial portions of the Software. ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ;;; DEALINGS IN THE SOFTWARE. ;; The decoder in the cl-json package didn't work the way I needed it ;; to, hence this code which is mostly stolen from that package. (in-package :clouchdb) (defvar *json-symbols-package* (find-package 'keyword) "The package where json-symbols are interned. Default keyword, nil = current package") ;; (defun json-intern (string) ;; (if *json-symbols-package* ;; (intern (camel-case-to-lisp string) *json-symbols-package*) ;; (intern (camel-case-to-lisp string)))) (defun json-intern (string) (as-keyword-symbol string)) (defparameter *json-rules* nil) (defparameter *json-object-factory* #'(lambda () nil)) (defparameter *json-object-factory-add-key-value* #'(lambda (obj key value) (push (cons (json-intern key) value) obj))) (defparameter *json-object-factory-return* #'(lambda (obj) (nreverse obj))) (defparameter *json-make-big-number* #'(lambda (number-string) (format nil "BIGNUMBER:~a" number-string))) (define-condition json-parse-error (error) ()) (defparameter *json-lisp-escaped-chars* `((#\" . #\") (#\\ . #\\) (#\/ . #\/) (#\b . #\Backspace) (#\f . ,(code-char 12)) (#\n . #\Newline) (#\r . #\Return) (#\t . #\Tab))) (defparameter *use-strict-json-rules* t) (defun json-escaped-char-to-lisp(json-escaped-char) (let ((ch (cdr (assoc json-escaped-char *json-lisp-escaped-chars*)))) (if *use-strict-json-rules* (or ch (error 'json-parse-error)) (or ch json-escaped-char)))) (defun lisp-special-char-to-json(lisp-char) (car (rassoc lisp-char *json-lisp-escaped-chars*))) (defun decode-json-from-string (json-string) (with-input-from-string (stream json-string) (decode-json stream))) (defun decode-json (&optional (stream *standard-input*)) "Reads a json element from stream" (funcall (or (cdr (assoc (peek-char t stream) *json-rules*)) #'read-json-number) stream)) (defun decode-json-strict (&optional (stream *standard-input*)) "Only objects or arrays on top level, no junk afterwards." (assert (member (peek-char t stream) '(#\{ #\[))) (let ((object (decode-json stream))) (assert (eq :no-junk (peek-char t stream nil :no-junk))) object)) ;;----------------------- (defun add-json-dispatch-rule (character fn) (push (cons character fn) *json-rules*)) (add-json-dispatch-rule #\t #'(lambda (stream) (read-constant stream "true" t))) (add-json-dispatch-rule #\f #'(lambda (stream) (read-constant stream "false" nil))) (add-json-dispatch-rule #\n #'(lambda (stream) (read-constant stream "null" nil))) (defun read-constant (stream expected-string ret-value) (loop for x across expected-string for ch = (read-char stream nil nil) always (char= ch x) finally (return ret-value))) (defun read-json-string (stream) (read-char stream) (let ((val (read-json-chars stream '(#\")))) (read-char stream) val)) (add-json-dispatch-rule #\" #'read-json-string) (defun read-json-object (stream) (read-char stream) (let ((obj (funcall *json-object-factory*))) (if (char= #\} (peek-char t stream)) (read-char stream) (loop for skip-whitepace = (peek-char t stream) for key = (read-json-string stream) for separator = (peek-char t stream) for skip-separator = (assert (char= #\: (read-char stream))) for value = (decode-json stream) for terminator = (peek-char t stream) for skip-terminator = (assert (member (read-char stream) '(#\, #\}))) do (setf obj (funcall *json-object-factory-add-key-value* obj key value)) until (char= #\} terminator))) (funcall *json-object-factory-return* obj))) (add-json-dispatch-rule #\{ #'read-json-object) (defun read-json-array (stream) (read-char stream) (if (char= #\] (peek-char t stream)) (progn (read-char stream) nil) (loop for first-in-element = (assert (not (member (peek-char t stream) '(#\, #\])))) for element = (decode-json stream) for terminator = (peek-char t stream) for skip-terminator = (assert (member (read-char stream) '(#\, #\]))) collect element until (char= #\] terminator)))) (add-json-dispatch-rule #\[ #'read-json-array) (defparameter *digits* '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) (defparameter *json-number-valid-chars* (concatenate 'list *digits* '(#\e #\E #\. #\+ #\-))) (defun read-json-number (stream) (let ((number-string (read-chars-until stream :terminator-fn #'(lambda (ch) (not (member ch *json-number-valid-chars*)))))) (assert (if (char= (char number-string 0) #\0) (or (= 1 (length number-string)) (char= #\. (char number-string 1))) t)) (handler-case (read-from-string number-string) (serious-condition (e) (let ((e-pos (or (position #\e number-string) (position #\E number-string)))) (if e-pos (handler-case (read-from-string (substitute #\l (aref number-string e-pos) number-string)) (serious-condition () (funcall *json-make-big-number* number-string))) (error "Unexpected error ~S" e))))))) (defun read-chars-until(stream &key terminator-fn (char-converter #'(lambda (ch stream) (declare (ignore stream)) ch))) (with-output-to-string (ostr) (loop (let ((ch (peek-char nil stream nil nil))) (when (or (null ch) (funcall terminator-fn ch)) (return)) (write-char (funcall char-converter (read-char stream nil nil) stream) ostr))))) (defun read-n-chars (stream n) (with-output-to-string (ostr) (dotimes (x n) (write-char (read-char stream) ostr)))) (defun read-json-chars(stream terminators) (read-chars-until stream :terminator-fn #'(lambda (ch) (member ch terminators)) :char-converter #'(lambda (ch stream) (if (char= ch #\\) (if (char= #\u (peek-char nil stream)) (code-char (parse-integer (read-n-chars stream 5) :start 1 :radix 16)) (json-escaped-char-to-lisp (read-char stream))) ch)))) From peddy at common-lisp.net Sat Dec 29 20:03:42 2007 From: peddy at common-lisp.net (peddy) Date: Sat, 29 Dec 2007 15:03:42 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071229200342.5ED921F00E@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv7071/src Modified Files: package.lisp clouchdb.lisp Log Message: Added hooks for document put/fetch actions --- /project/clouchdb/cvsroot/clouchdb/src/package.lisp 2007/12/28 16:25:51 1.4 +++ /project/clouchdb/cvsroot/clouchdb/src/package.lisp 2007/12/29 20:03:42 1.5 @@ -30,6 +30,8 @@ :*host* :*port* :*db-name* + :*document-update-fn* + :*document-fetch-fn* :as-keyword-symbol :as-field-name-string :db-existential-error @@ -45,6 +47,7 @@ :with-connection :document-properties :document-property + :set-document-property :list-dbs :create-db :delete-db --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/28 16:25:51 1.15 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/29 20:03:42 1.16 @@ -29,6 +29,8 @@ "As of version 7.2, the IANA assigned CouchDb port (was 8888)") (defvar *db-name* "default" "Default database name") (defvar *protocol* "http" "http or https") +(defvar *document-update-fn* nil) +(defvar *document-fetch-fn* nil) (defvar *text-types* '(("text" . nil) @@ -39,7 +41,7 @@ (defparameter *temp-db-counter* 0 "Used in the creation of temporary databases") (defmacro define-constant (name value &optional doc) - "A version of DEFCONSTANT for, cough, /strict/ CL implementations." + "A version of DEFCONSTANT for /strict/ CL implementations." ;; See `(defconstant ,name (if (boundp ',name) (symbol-value ',name) ,value) ,@(when doc (list doc)))) @@ -47,6 +49,9 @@ (define-constant +utf-8+ (make-external-format :utf-8 :eol-style :lf) "Default external format for document content.") +(defun document-update-notify (fn doc) + (if fn (funcall fn doc) doc)) + ;; ;; URL Parameter helpers ;; @@ -275,7 +280,15 @@ (cond ((hash-table-p doc) (setf (gethash name doc) value)) (t (rplacd (assoc name doc) value))) - value)) + doc)) + +(defun set-document-property (doc name value) + "Set a property of a document. If the named property does not exist, +create it otherwise modify the existing value. May or may not +destructively modify document, so be sure to use return value." + (if (assoc name doc) + (setf (document-property name doc) value) + (cons `(,(as-keyword-symbol name) . ,value) doc))) (defun db-request (uri &rest keys &key &allow-other-keys) "Used by all Couchdb APIs to make the actual REST request." @@ -295,7 +308,9 @@ ;; (defun set-connection (&key (host nil host-p) (db-name nil db-name-p) - (protocol nil protocol-p) (port nil port-p)) + (protocol nil protocol-p) (port nil port-p) + (document-update-fn nil document-update-fn-p) + (document-fetch-fn nil document-fetch-fn-p)) "Set top-level connection information. The port may be specified as a string or number. As of CouchDb version 7.2 the default port is 5984, prior to that it was 8888." @@ -303,15 +318,18 @@ (when db-name-p (setf *db-name* db-name)) (when port-p (setf *port* (value-as-string port))) (when protocol-p (setf *protocol* protocol)) + (when document-update-fn-p (setf *document-update-fn* document-update-fn)) + (when document-fetch-fn-p (setf *document-fetch-fn* document-fetch-fn)) (values)) -(defmacro with-connection ((&rest args &key db-name port protocol host) +(defmacro with-connection ((&rest args &key db-name port protocol host + document-update-fn document-fetch-fn) &body body) "Execute body in the context of the optionally specified host, db-name, port or protocol. Port may be a string or a number, protocol is http or https. As of CouchDb version 7.2 the default port is 5984, prior to that it was 8888." - (declare (ignore db-name port protocol host)) + (declare (ignore db-name port protocol host document-update-fn document-fetch-fn)) `(let (,@(loop for var on args by #'cddr collect (list (keyword-to-special (car var)) (second var)))) , at body)) @@ -449,7 +467,7 @@ ((and if-missing-p (not (eq if-missing :error))) if-missing) (t (error 'document-missing :id id)))) - res)))) + (document-update-notify *document-fetch-fn* res))))) (defun put-document (doc &key id) "Create a new document or update and existing one. If the document @@ -465,19 +483,21 @@ ;; If an ID was specified and that ID does not match the ;; :_id property of the current document, strip the document ;; of all special (CouchDb managed) properties, since these - ;; are specific to the current document. In this case, the - ;; presence of the ID parameter means 'create a new document - ;; with the same contents as the old one'. + ;; are specific to the current document. The presence of the + ;; ID parameter in this situation means 'create a new + ;; document with the same contents as the old one'. ((and id current-id (not (equal current-id id))) (setf doc (document-properties doc)))) (let ((res (ensure-db () - (db-request (cat (url-encode *db-name*) "/" + (db-request (cat (url-encode *db-name*) "/" (url-encode (if id id current-id))) :content-type "text/javascript" :external-format-out +utf-8+ :content-length nil - :content (document-to-json doc) - :method :put)))) + :content (document-to-json + (document-update-notify + *document-update-fn* doc)) + :method :put)))) (when (document-property :|error| res) (error (if (equal "conflict" (document-property :|error| res)) 'id-or-revision-conflict 'doc-error) @@ -486,14 +506,16 @@ res))) (defun post-document (doc) - "Create a document and let the server assign an ID. A successful -areturn value includes the new document ID, in the :ID property." + "Put the potentially modified document back on the server or, if the +document contains no ID, create a document and let the server assign +one. The return value includes the document ID in the :ID property." (let ((res (ensure-db () (db-request (cat (url-encode *db-name*) "/") :content-type "text/javascript" :external-format-out +utf-8+ :content-length nil - :content (document-to-json doc) + :content (document-to-json + (document-update-notify *document-update-fn* doc)) :method :post)))) (when (document-property :|error| res) (error 'doc-error) :id nil :reason (document-property :|reason| res)) From peddy at common-lisp.net Sat Dec 29 21:20:28 2007 From: peddy at common-lisp.net (peddy) Date: Sat, 29 Dec 2007 16:20:28 -0500 (EST) Subject: [clouchdb-cvs] CVS clouchdb/src Message-ID: <20071229212028.D05531E0D1@common-lisp.net> Update of /project/clouchdb/cvsroot/clouchdb/src In directory clnet:/tmp/cvs-serv19849/src Modified Files: tests.lisp clouchdb.lisp Log Message: Add updated tests tests for field name change --- /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/28 16:25:51 1.8 +++ /project/clouchdb/cvsroot/clouchdb/src/tests.lisp 2007/12/29 21:20:28 1.9 @@ -111,10 +111,10 @@ (deftestsuite clouchdb-general-tests (clouchdb-tests) () ()) (addtest (clouchdb-general-tests) - (:documentation "Test document-property") + (:documentation "Ensure document-property gets correct value from document") general-tests-document-property (ensure - (let ((doc '((:NAME . "name1") (:|Name| . "name2") (:|NaMe| . "NaMe")))) + (let ((doc '((:NAME . "Value1") (:|Name| . "Value2") (:|NaMe| . "Value3")))) (reduce #'(lambda (a b) (and a b)) (mapcar #'(lambda (e) (equal (cdr (assoc (car e) doc)) @@ -122,36 +122,6 @@ doc))))) (addtest (clouchdb-general-tests) - (:documentation "Test document-property using property name strings") - general-tests-document-property-string - (ensure-same "name1" - (document-property "name" '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) - (ensure-same "name2" - (document-property "Name" '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) - (ensure-same "name3" - (document-property "NaMe" '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3"))))) - -(addtest (clouchdb-general-tests) - (:documentation "Test document-property using keyword symbols") - general-tests-document-property-keyword - (ensure-same "name1" - (document-property :NAME '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) - (ensure-same "name2" - (document-property :-NAME '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) - (ensure-same "name3" - (document-property :-NA-ME'((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3"))))) - -(addtest (clouchdb-general-tests) - (:documentation "Test document-property using non-keyword symbols") - general-tests-document-property-symbol - (ensure-same "name1" - (document-property 'name '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) - (ensure-same "name2" - (document-property '-name '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3")))) - (ensure-same "name3" - (document-property '-na-me '((:NAME . "name1") (:-NAME . "name2") (:-NA-ME . "name3"))))) - -(addtest (clouchdb-general-tests) (:documentation "Test case-encoded field name functions") general-tests-case-encoded (ensure-same "lowercase" (as-field-name-string (as-keyword-symbol "lowercase"))) --- /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/29 20:03:42 1.16 +++ /project/clouchdb/cvsroot/clouchdb/src/clouchdb.lisp 2007/12/29 21:20:28 1.17 @@ -50,6 +50,8 @@ "Default external format for document content.") (defun document-update-notify (fn doc) + "Optionally invoke specified function with supplied document, used + to invoke user-specified hook functions." (if fn (funcall fn doc) doc)) ;;