I suggest using couchapp to store your field meta-info (validation,
format, etc) in a config/ folder or file, then you can:
a) Include it into your validate_doc_update.js file like:
function (newDoc, oldDoc, userCtx) {
// !json config.form
b) Refer to it from elsewhere like:
(PHP, because that is what I'm using)
$config = $gateway->get('_design/couchapp')->config->form;
So if you have a bunch of required fields, validation_doc_update can
process them like:
for(field in config.form.fields) {
...
if (field.required == true && !(field.name in newDoc))
throw({forbidden:"Missing required field"});
...
And your application can use them like:
foreach($config->form->fields as $f) {
...
$e = $form->createElement($f->type, $f->name)
...
->setRequired($f->required);
}
Obviously there's plenty of application-specific design work, but this
sort of pattern allows you to store lots of validation info in a way
that you can store it in the db (where it belongs), and still access it
within the application.
Works for me,
-Patrick
On 10/03/2011 11:00 AM, Simeon F. Willbanks wrote:
Justin,
Thank you for the reply and helping me brainstorm a solution. I like
the !code macro idea, but my CouchDB server isn't on the same machine
as my application. They communicate through a network. Could I do
this?
function(newDoc, oldDoc, userCtx) {
//!code path/to/validate.py
validate(newDoc, oldDoc, userCtx);
}
path/to/validate.py would be network aware and would return valid JavaScript.
Thanks,
Simeon
On Wed, Mar 9, 2011 at 1:27 PM, Justin Walgran<[email protected]> wrote:
Simeon,
the !code macro in CouchApp was created so you can DRY up your
functions in the exact way you want. If you extract your validator,
you can include it in both your validate_doc_update function and your
client code. Something like this:
function(newDoc, oldDoc, userCtx) {
//!code path/to/validate.js
validate(newDoc, oldDoc, userCtx);
}
// Content of validate.js
var validate = function(newDoc, oldDoc, userCtx) {
var Errors = {count: 0};
var Message = function(field, type, text) {
this.type = type || 'string';
this.text = text || field + ' is required';
this.required = true;
};
var require = function(field, type, text) {
if (!newDoc[field]) {
Errors[field] = new Message(field, type, text);
Errors.count += 1;
};
};
if (newDoc.type == 'post') {
require('title');
require('created_at', 'datetime');
require('body');
require('author');
};
if (newDoc.type == 'comment') {
require('name');
require('created_at', 'datetime');
require('comment', 'string', 'You may not leave an empty
comment');
};
if (Errors.count> 0) {
throw({forbidden: JSON.stringify(Errors)});
};
}
If you refactor a little further, you could go the next step and
generate a form from the configuration of the validator without the
making the extra, intentionally bad request to the server.
Justin
On Wed, Mar 9, 2011 at 4:10 PM, Simeon F. Willbanks<[email protected]> wrote:
From the 'CouchDB The Definitive Guide', "CouchDB uses the
validate_doc_update function to prevent invalid or unauthorized
document updates from proceeding." This clearly defines the
validate_doc_update function's responsibility. That said, can we
extend this responsibility a bit? I'd rather not redefine valid
document attributes in my external application since
validate_doc_update is already doing the work (DRY). Maybe the
application can query the validate_doc_update function for the exact
attributes of a valid document. Here is a proof of concept
validate_doc_update function.
function(newDoc, oldDoc, userCtx) {
Errors = {count: 0};
function Message(field, type, text) {
this.type = type || 'string';
this.text = text || field + ' is required';
this.required = true;
};
function require(field, type, text) {
if (!newDoc[field]) {
Errors[field] = new Message(field, type, text);
Errors.count += 1;
};
};
if (newDoc.type == 'post') {
require('title');
require('created_at', 'datetime');
require('body');
require('author');
};
if (newDoc.type == 'comment') {
require('name');
require('created_at', 'datetime');
require('comment', 'string', 'You may not leave an empty comment');
};
if (Errors.count> 0) {
throw({forbidden: JSON.stringify(Errors)});
};
}
In my application, before putting a new document or building a
document input UI (web form), I can "query" the validate_doc_update
function like so:
$ curl -X PUT couchdb:5984/basic/a1a0d5f1e202b48e5bc55f616d0021ff -d
'{"type":"post"}'
{"error":"forbidden","reason":"{\"count\":4,\"title\":{\"type\":\"string\",\"text\":\"title
is
required\",\"required\":true},\"created_at\":{\"type\":\"datetime\",\"text\":\"created_at
is required\",\"required\":true},\"body\":{\"type\":\"string\",\"text\":\"body
is
required\",\"required\":true},\"author\":{\"type\":\"string\",\"text\":\"author
is required\",\"required\":true}}"}
From the response, I can decode the JSON object's reason property to a
useful array.
array
'count' => int 4
'title' =>
array
'type' => string 'string' (length=6)
'text' => string 'title is required' (length=17)
'required' => boolean true
'created_at' =>
array
'type' => string 'datetime' (length=8)
'text' => string 'created_at is required' (length=22)
'required' => boolean true
'body' =>
array
'type' => string 'string' (length=6)
'text' => string 'body is required' (length=16)
'required' => boolean true
'author' =>
array
'type' => string 'string' (length=6)
'text' => string 'author is required' (length=18)
'required' => boolean true
From this array, I can build the input UI or validate the new document
before putting.
To reiterate, I am trying to DRY up the validation
rules/responsibilities by defining them in one spot. This proof of
concept uses the validate_doc_update function in CouchDB. The
validate_doc_update function is now responsible for preventing invalid
documents from being PUT to CouchDB, and it can respond to application
queries for what constitutes a valid document.
So, is this a bad idea? Is there a more idiomatic way to accomplish this goal?
Thanks,
Simeon