Paul Roub

A Software Tool Geek in His Natural Habitat

Building a Veracity Module - Part 2

Last time, we installed a Wiki module for Veracity. This time, we’ll look at the pieces that make a module work.

Veracity modules can add to several different parts of the Veracity infrastructure; not every module will touch all of these. They are:

  1. New templated data types (in our case, Wiki pages), and the database DAGs to hold them.
  2. Server-side Javascript code, run in response to either:
    1. Web server requests (creating, updating, viewing Wiki pages), or
    2. Version control hooks (not used in this example)
  3. New Veracity web pages.
  4. Client-side Javascript, run as part of Veracity’s Web UI.

The client-side Javascript is found under server_files/ui/modules/wiki; the rest lives under server_files/modules/wiki. A portion of init.js in that folder bears closer inspection:

1
2
3
4
5
6
7
8
9
area: "wiki",
vendor: sg.vendor.SOURCEGEAR,
grouping: 5,
dagnums: {
    "WIKI": {
        dagid: 1,
        template: 'sg_ztemplate__wiki.json'
    }
},

We’re creating a new database “area” - a group of DAGs that are related to one another. Veracity ships with areas like version_control, and the scrum module defines (not surprisingly) scrum. For modules, the area name should match the containing folder name.

Each module definition needs a vendor ID. Right now, that’s just us. sg.vendor.SOURCEGEAR == 1. If you’re adding your own areas, get in touch, and I’ll make sure you have a vendor ID that doesn’t conflict with anyone else’s.

The grouping property is the number of this area within the vendor’s space. 1-4 were already used (including Scrum), so I added 1. Clever me.

Similarly, dagid is the number of each DAG within this area. If you looked at the scrum module, you’d see the WORK_ITEMS dag has dagid == 1, and the BUILDS dag has dagid == 2.

Every DAG in Veracity needs a template - a description of its record types, their fields, and the merging rules involved for each. All rectypes must be fully, automatically mergeable - failure is, literally, not an option. This allows distributed databases and their owners to remain sane. Merge strategies include “last first”, “greatest”, “uniqify”, etc. We’ll look at the Wiki’s choice next time.

In our init file, we specify the JSON file describing each database template. The templates for core Veracity DAGs can be found in @/src/libraries/templates.

At some magical hand-wavy time that you needn’t worry about, Veracity will look at this init file and:

  1. Make sure the repo we’re playing with has this area installed
  2. Make sure that area has the DAGs we need
  3. Make sure that the DAGs have the right templates set

Once this has happened, the DAGs are available for Javascript use just like the built-in types. If you run the vscript interpreter on a wiki-enabled repo, you can see this:

vscript> sg.to_json__pretty_print( repo.list_areas() )
{
    "core" : 257,
    "version_control" : 258,
    "scrum" : 259,
    "wiki" : 261
}

vscript> sg.to_json__pretty_print(sg.dagnum)
{
    "VERSION_CONTROL" : "0000000010201001",
    "USERS" : "0000000010102062",
    "AREAS" : "0000000010101042",
    "VC_COMMENTS" : "00000000102021c2",
    "VC_TAGS" : "00000000102040c2",
    "VC_BRANCHES" : "0000000010205142",
    "VC_STAMPS" : "00000000102031c2",
    "VC_HOOKS" : "00000000102071c2",
    "TESTING_DB" : "0000000010401002",
    "TESTING2_DB" : "0000000010402002",
    "WORK_ITEMS" : "0000000010301002",
    "BUILDS" : "0000000010302002",
    "WIKI" : "0000000010501002"
}

vscript> db = new zingdb(repo, sg.dagnum.WIKI)
[object zingdb]
vscript> sg.to_json__pretty_print( db.get_template() )
{
    "version" : 1,
    "rectypes" : 
    {
        "page" : 
        {
            "merge" : 
            {
                "merge_type" : "field",
            // ...
            }
        // ...
        }
    }
    // ...
}

And after creating a page or two:

vscript> db = new zingdb(repo, sg.dagnum.WIKI)
vscript> records = db.query('page', ['*'])
[object Object],[object Object]
vscript> sg.to_json__pretty_print(records[1])
{
    "recid" : "ge9dbadde62004708abd960d58a99753f191e0f24c42b11e0a1a0c8bcc8e13b9a",
    "text" : "This is, in fact, another page entirely.  \n\n[[Yet Another Page]]",
    "title" : "Another Page"
}

Veracity will also install any URIs added by the module’s server-side Javascript. Look at server_files/modules/wiki/wiki.js to see how those are specified:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"/repos/<repoName>/wiki/page/<pagename>.json": {
    "GET": {
        onDispatch: function (request) {
           // this handles the request for a Wiki page's current contents
           // ...
       }
    }
},

"/repos/<repoName>/wiki/page": {
    "POST": {
        onJsonReceived: function (request, newrec) {
           // here we receive JSON (in newrec) describing a page to be updated or created
           // ...
        }
     }
}

These are used in Ajax calls from server_files/ui/modules/wiki/wiki.js. For example, when rendering links to other wiki pages, we validate those links by attempting to retrieve them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var purl = sgCurrentRepoUrl + "/wiki/page/" + pageName + ".json";

vCore.ajax(
{
    url: purl,
    dataType: 'json',
    reportErrors: false,
    success: function(data) {
        vvWiki.setGoodPage(ln, pageName);
    },
    error: function() {
        vvWiki.setBadPage(ln, pageName);
    }
});

Next up: we follow the code through the creation, update, and merge of a Wiki page.