Tuesday, 18 January 2011
You may not realise that CouchDB actually supports CommonJS modules in design documents. This can be very useful when you want to share code across different list, show, update or validation functions. Its also a useful way to share code between the client and server by using a CommonJS implementation for the browser. Normally you would store client-side code in an attachment, but by storing it on the design doc you can access it server-side too!
To add a module to a design document you need to store it as a string (much as you would for any other function). Modules can be stored under any property name:
You'll notice that modules can be stored under a hierachy of properties. The above design document would allow you to require 'lib/mymodule' or 'mymodule2'.
Paths are from the root of the design doc, unless explicitly relative. Relative paths start with '..' or '.' and are relative to the current module. So, from 'lib/mymodule' in the previous example, you could do require('../mymodule1'). Let's make the example design doc a little more confusing!
Now, if from 'lib/mymodule' we were to do require('mymodule2').name, what value would be returned? In this case it would be 'my module 2', not 'new module', since the path is not explicitly relative. You cannot require modules from another design document. This is as you would expect from CouchDB.
You can't require modules from within view (map/reduce) functions. This is because CouchDB needs to know when the behaviour of a view changes, so it can rebuild the list of results. However, in the upcoming CouchDB 1.1.x views will be able to require modules provided they exist below the 'views' property (eg, 'views/lib/module'):
Currently, CouchDB does not support caching of eval'd modules. That means requiring a complex module evaluates it every time. This is obviously quite inefficient, and there is an ticket in JIRA to fix this.
One additional problem with not having a module cache is that modules cannot store state between requires. This technique can sometimes be used to cache function results, or to store a registry of templates or settings. Be careful when using libraries that depend on this.
CouchDB initially implemented Modules 1.0 in the 0.11.0 release. This was later upgraded to Modules 1.1.1 in version 0.11.1. Modules 1.1.1 was developed by Mikeal and the CommonJS team to overcome some problems that made Modules 1.1 difficult to implement in CouchDB.
Using module.exports to change the exported object entirely can be quite useful, but was not added until after the Modules 1.0 spec. That means any code which requires this will not run on CouchDB 0.11.0 or earlier! For example, using module.exports, you could have a commonjs module export a string instead of an object:
If you've found this interesting or useful, you might want to follow the progress of my experiments on pushing the module system further at kan.so.