Diligence is still under development and incomplete, and some this documentation is wrong. For a more comprehensive but experimental version download the Savory Framework, which was the preview release of Diligence.
Documents Service
This service lets you store versioned HTML documents in MongoDB using your choice among several markup languages: Markdown, Textile, Confluence, MediaWiki, TWiki and Trac. It's thus an essential building block for CMS features, such as wikis and blogs.
Every "document" in this service is indeed a single MongoDB document, but internally it composed of versioned "drafts". The last draft (of the highest revision number) represents the current state of the document. For efficiency, the last draft in its own key ("activeDraft"), allowing you to retrieve it from MongoDB without retrieving the whole history of drafts, which is an array. Additionally, each draft is stored both as markup source code and as rendered result, so that rendering only happens once.
MongoDB atomic operations guarantee that even if more than one person is revising a document at the same, no draft will be lost. Only last update to come in, though, will get to set the "activeDraft" key.
Documents are associated with a "site", of which there must be at least one. The Document Service can handle many "sites" at once, each with its own set of documents. The versioning system is designed to be global per each site, meaning that all drafts associated with a site will have serial and unique revision numbers per that site. This allows time travel: you that you can view the entire state of a site at a given time by fetching only drafts smaller than a certain revision. (This also implies that every draft as its own unique revision number, but there's no easy way in MongoDB to traverse drafts in this order.)
Note the markup rendering is handled by the HTML service, which you can choose to use directly if you do not need the versioning system.
Usage
Make sure to check out the API documentation for Diligence.Documents.
The API doesn't actually encourage you to access "documents" directly. Instead, you access "drafts" via the document ID and its revision, or simply request the latest draft. As stated above, the API is designed to be very efficient in doing this: whether it's the latest draft you need or a specific older revision, it's a very direct MongoDB fetch.
To fetch the latest draft by the document ID and print out its rendered HTML:
<html> <% document.executeOnce('/diligence/service/documents/') var draft = Diligence.Documents.getDraft('4fc4457ae4b030c6611c072f') %> <body> <%= draft.render() %> </body> </html>
Efficiency note: if that particular draft has already been rendered once, the render() call won't do anything at all, the rendered version having already been fetched. Other options for fetching drafts: you can also call "getLatestDraft" with a maximum revision number, or just call "getDraft" with a specific revision number you want.
To revise a draft, meaning that you will add a new revision to the document:
draft.revise('this is the markup source', 'textile')
Note that after revision, the draft object is updated with the new information. So you can call "draft.getRevision()" to see the new revision number if you need it. Again, remember that this particular revision number will be unique for the entire "site": no other document or draft will have it.
To create a new document:
var site = Diligence.Documents.getSite('4d5595e3f7f2d14d2ab9630f') var draft = site.createDocument('a new document!', 'textile')
Note that "createDocument" returns a draft object, which will be the first and only draft of the document.
As you you can see, the usage is simple and efficient, but the implementation does have some sophistication. It's recommended that you look at the MongoDB collections for "documents" and "sites" to get a sense of how they work together.
Integration
To integrate the Documents Service into your application, use the document ID by called "getDocumentId()" on a draft object, and then store that ID in your own structure. For example, if you're writing a wiki, you might want to associate a wiki page with that document ID. Similarly for a blog entry. And, of course, this is schema-free MongoDB: feel free to add whatever data you need to your "document" documents. You can also inherit the Document Service classes and add the necessary functionality.
An important feature of wiki markup languages is support for special processing of wiki page references, turning them into HTML hyperlinks and possibly creating the page in the wiki. The Documents Service lets you hook in your code to support custom delimiters, so it can output proper links. Example:
var rendered = draft.render({ codes: { start: '{{', end: '}}', fn: function(text) { return '<a href="/link/{0}">{0}</a>'.cast(text) } } })
You could then insert these custom codes in your markup:
This is a link to {{mywikitopic}}.
The "codes" key can be an array of several such code processors, and the function can output anything at all, not just links, so you can use it to extend the markup language. In fact, the function can actually do something more substantial than output: you could, for example, save a cross reference to the remote wiki page, or create an empty template for a non-existing page.
Note that custom code processing happens only during the first render: in subsequent calls to "render()" on this draft the "codes" argument will be ignored.
Configuration
If you like, you can avoid specifying the markup language in all the API calls. The default language would then be "textile", but you can change it in your application's "settings.js" by adding something like this to your app.globals:
app.globals = { ... diligence: { service: { documents: { defaultLanguage: 'markdown' } } } }
The Diligence Manual is provided for you under the terms of the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. The complete manual is available for download as a PDF.