m src/install.rdf.in
maxVersion upped
Various things I'm saying and doing in various places
m src/install.rdf.in
maxVersion upped
m src/Makefile
upped version to 1.5
Paul Roub posted a photo:
A little butter, a little smoked sea salt... and fresh whole wheat bread from my dutch oven, per Michael Ruhlman's "Bread Baking Basics" iPad app.
And good lord, that was easy.
by Paul Roub (nobody@flickr.com) at January 22, 2012 10:06 PM
I recently lucked into a Takamine G-335 12-string in a well-timed eBay bid. I'm very happy with it, but sitting down? On stage? This will not do. But there's just the one strap button at the butt end, and I'm not tying a strap to the headstock; that maybe looked cool on Elvis. Maybe.
I'd been meaning to put strap locks on my Strat, which would leave me with a spare strap button or two.
So:
And then...
Safety first...
Et voilà.
It is at this point that I realize I don't yet have a strap for this guitar.
So close...
If you visited openmikes.org on March 18, 2012 (just barely yesterday in my time zone, as I write this), you noticed that the site was missing. Unless openmikes.org is the only site you visited that day, you are probably aware that this was not an isolated incident. Wikipedia, Boing Boing and numerous others went “dark” to protest the SOPA and PIPA bills being considered by the US Congress.
Sorry for the inconvenience, but this site reaches more people in a day than my personal blog does in a month. Far more. If I was going to make a point, this was the place.
As a musician, a songwriter, a creator of works I do want protected… SOPA isn’t the way to do it.
I heartily recommend reading this interview with publisher Tim O’Reilly for an excellent explanation of what SOPA is, and why it’s more dangerous than any problem it would allegedly solve.
m src/.gitignore m src/chrome/content/ieviewOverlay.js m src/chrome/content/ieviewsettings.xul m src/chrome/content/reloaded.html
migrated preferences from ieview.* to extensions.ieview.* per #26
Paul Roub posted a photo:
Serial numbers were running 3 or 4 years ahead at this point. It's most likely an '81, but might be an '80.
by Paul Roub (nobody@flickr.com) at January 06, 2012 05:48 PM
Paul Roub posted a photo:
OK, the pickup switch knob was swapped out the day I bought it, from the Eagle hanging next to it. The original was cream, I thought black looked better. :-)
by Paul Roub (nobody@flickr.com) at January 06, 2012 05:48 PM
Paul Roub posted a photo:
There's a bit more buckle rash than is shown, almost all of it from the month between the guitar's theft and recovery in '84.
by Paul Roub (nobody@flickr.com) at January 06, 2012 05:47 PM
75 N. Orlando Ave.
Cocoa Beach, FL
321-784-4044
https://www.facebook.com/pages/Juice-N-Java-Cafe/282952588728
75 N. Orlando Ave.
Cocoa Beach, FL
321-784-4044
https://www.facebook.com/pages/Juice-N-Java-Cafe/282952588728
Just a quick post to note that we’ve released Veracity 1.5.
The most-visible new features:
More details to come soon, but I have a race condition to bang my head against.
Meantime, checkout the release notes and downloads.
Considered harmful
Declarative statement opening statement blog. Stand back a little declarative statement, then double-pivot down with even more controversial declarative statement. Insult programming language beloved aesthetic idioms and assert more recent versions as superior. Including the statistics support that lack the Y-axis labels to shore up weak link bait, including declarative statement. Linus reference, invoke Dijkstra. Oh, everything looks bad if you remember it.
Benchmarks biased
While supporting misguided statement. Weak marginally misleading statement because I want to be on TechCrunch. Farmville.
If 1, then A
else if 2 then B
else if 3 then C
else / D otherwise
K & RC Support cite page references induction with reference to non sequitur Mythical Man Month.
Use the right tool for the right job. Grep more difficult. Appeal to authority. Fowler invocation stating Turing completeness. This is not NP complete. Try harder to grok.
UR doing it wrong
I suggest you try again. This time, let go your conscious self and act on instinct. Finding a needle in a haystack is not difficult when each straw is computerized. Spare me your space age technobabble, Attila the Hun! Soothe us with sweet lies.
There is one cat on the computer. He is running a custom Mint Distro. This is a meme!
Use the right tool for the right job. Srsly. Down-voted into oblivion. Spolsky obscure link justifying the opposite position .. Atwoodian is a tautology.
Accusations of intellectual dishonesty
Off-handed reference to FizzBuzz reminiscent of the dream of pearls programming reference for my first Altair and frustration with the status quo.
CmdrTaco name down, dig in Digg, Y-Combinator sour grapes. I do not think it was physically possible but this both sucks and blows * *. Yes, if you make it look like an electrical fire. It is a kart, powered by my own sense of self-satisfaction.
Reference to the diagram of useless and misplaced frustration at Large Companies.Comments directed to patent trolls combined with witty acerbic statement concerning the uselessness Lessig and software patents.
Random bold concluding sentence.
Follow me. Read my blog. I have opinions.
+ old-src/Makefile + old-src/adjustbuild.pl + old-src/bt + old-src/bumpbuild + old-src/checklocale.pl + old-src/checkversion.pl + old-src/creditgen + old-src/creditgen.pl + old-src/newver + old-src/skin/contents.rdf + old-src/testlocale + old-src/xmlcheck.pl + src/Makefile + src/chrome.manifest.in m src/chrome/content/ieviewOverlay.js m src/chrome/content/ieviewOverlay.xul + src/chrome/content/ieviewsettings.xul + src/chrome/content/reloaded.html + src/chrome/locale/bg-BG/ieview.dtd + src/chrome/locale/bg-BG/ieview.properties + src/chrome/locale/ca-AD/ieview.dtd + src/chrome/locale/ca-AD/ieview.properties + src/chrome/locale/cs-CZ/ieview.dtd + src/chrome/locale/cs-CZ/ieview.properties + src/chrome/locale/da-DK/ieview.dtd + src/chrome/locale/da-DK/ieview.properties + src/chrome/locale/de-DE/ieview.dtd + src/chrome/locale/de-DE/ieview.properties + src/chrome/locale/el-GR/ieview.dtd + src/chrome/locale/el-GR/ieview.properties + src/chrome/locale/en-US/ieview.dtd + src/chrome/locale/en-US/ieview.properties + src/chrome/locale/es-AR/ieview.dtd + src/chrome/locale/es-AR/ieview.properties + src/chrome/locale/es-ES/ieview.dtd + src/chrome/locale/es-ES/ieview.properties + src/chrome/locale/eu-ES/ieview.dtd + src/chrome/locale/eu-ES/ieview.properties + src/chrome/locale/eu/ieview.dtd + src/chrome/locale/eu/ieview.properties + src/chrome/locale/fi-FI/ieview.dtd + src/chrome/locale/fi-FI/ieview.properties + src/chrome/locale/fr-FR/ieview.dtd + src/chrome/locale/fr-FR/ieview.properties + src/chrome/locale/fy-NL/ieview.dtd + src/chrome/locale/fy-NL/ieview.properties + src/chrome/locale/he-IL/ieview.dtd + src/chrome/locale/he-IL/ieview.properties + src/chrome/locale/hr-HR/ieview.dtd + src/chrome/locale/hr-HR/ieview.properties + src/chrome/locale/hu-HU/ieview.dtd + src/chrome/locale/hu-HU/ieview.properties + src/chrome/locale/it-IT/ieview.dtd + src/chrome/locale/it-IT/ieview.properties + src/chrome/locale/ja-JP/ieview.dtd + src/chrome/locale/ja-JP/ieview.properties + src/chrome/locale/km-KH/ieview.dtd + src/chrome/locale/km-KH/ieview.properties + src/chrome/locale/ko-KR/ieview.dtd + src/chrome/locale/ko-KR/ieview.properties + src/chrome/locale/lt-LT/ieview.dtd + src/chrome/locale/lt-LT/ieview.properties + src/chrome/locale/nb-NO/ieview.dtd + src/chrome/locale/nb-NO/ieview.properties + src/chrome/locale/nl-NL/ieview.dtd + src/chrome/locale/nl-NL/ieview.properties + src/chrome/locale/pl-PL/ieview.dtd + src/chrome/locale/pl-PL/ieview.properties + src/chrome/locale/pt-BR/ieview.dtd + src/chrome/locale/pt-BR/ieview.properties + src/chrome/locale/pt-PT/ieview.dtd + src/chrome/locale/pt-PT/ieview.properties + src/chrome/locale/ru-RU/ieview.dtd + src/chrome/locale/ru-RU/ieview.properties + src/chrome/locale/sk-SK/ieview.dtd + src/chrome/locale/sk-SK/ieview.properties + src/chrome/locale/sl-SI/ieview.dtd + src/chrome/locale/sl-SI/ieview.properties + src/chrome/locale/sr-RS/ieview.dtd + src/chrome/locale/sr-RS/ieview.properties + src/chrome/locale/sr-YU/ieview.dtd + src/chrome/locale/sr-YU/ieview.properties + src/chrome/locale/sv-SE/ieview.dtd + src/chrome/locale/sv-SE/ieview.properties + src/chrome/locale/th-TH/ieview.dtd + src/chrome/locale/th-TH/ieview.properties + src/chrome/locale/tr-TR/ieview.dtd + src/chrome/locale/tr-TR/ieview.properties + src/chrome/locale/uk-UA/ieview.dtd + src/chrome/locale/uk-UA/ieview.properties + src/chrome/locale/vi-VN/ieview.dtd + src/chrome/locale/vi-VN/ieview.properties + src/chrome/locale/zh-CN/ieview.dtd + src/chrome/locale/zh-CN/ieview.properties + src/chrome/locale/zh-TW/ieview.dtd + src/chrome/locale/zh-TW/ieview.properties + src/chrome/skin/ieview-button-16px-disabled.png + src/chrome/skin/ieview-button-16px.png + src/chrome/skin/ieview-button-24px-disabled.png + src/chrome/skin/ieview-button-24px.png + src/chrome/skin/ieview-icon.png + src/chrome/skin/ieview-toolbar-button.css + src/defaults/preferences/ieview.js + src/inslocales.pl + src/install.rdf.in + src/makemanifest.pl
rebuild ieview.xpi per modern extension standards
m src/rebuild/Makefile m src/rebuild/chrome/content/ieviewOverlay.js m src/rebuild/chrome/skin/ieview-button-16px-disabled.png m src/rebuild/chrome/skin/ieview-button-16px.png m src/rebuild/chrome/skin/ieview-button-24px-disabled.png m src/rebuild/chrome/skin/ieview-button-24px.png m src/rebuild/chrome/skin/ieview-icon.png m src/rebuild/inslocales.pl m src/rebuild/install.rdf.in m src/rebuild/makemanifest.pl
find tabContextMenu in recent FF versions
m src/.gitignore - src/chrome.manifest.firefox-autogen.txt - src/chrome/.cvsignore - src/content/contents.rdf - src/cvs2cl.pl - src/install.js.in - src/locale/.cvsignore - src/locale/bg-BG/ieview/contents.rdf - src/locale/ca-AD/ieview/contents.rdf - src/locale/cs-CZ/ieview/contents.rdf - src/locale/da-DK/ieview/contents.rdf - src/locale/de-DE/ieview/contents.rdf - src/locale/el-GR/ieview/contents.rdf - src/locale/en-US/ieview/contents.rdf - src/locale/es-AR/ieview/contents.rdf - src/locale/es-ES/ieview/contents.rdf - src/locale/eu-ES/ieview/contents.rdf - src/locale/eu/ieview/contents.rdf - src/locale/fi-FI/ieview/contents.rdf - src/locale/fr-FR/ieview/contents.rdf - src/locale/fy-NL/ieview/contents.rdf - src/locale/he-IL/ieview/contents.rdf - src/locale/hr-HR/ieview/contents.rdf - src/locale/hu-HU/ieview/contents.rdf - src/locale/it-IT/ieview/contents.rdf - src/locale/ja-JP/ieview/contents.rdf - src/locale/km-KH/ieview/contents.rdf - src/locale/ko-KR/ieview/contents.rdf - src/locale/lt-LT/ieview/contents.rdf - src/locale/nb-NO/ieview/contents.rdf - src/locale/nl-NL/ieview/contents.rdf - src/locale/pl-PL/ieview/contents.rdf - src/locale/pt-BR/ieview/contents.rdf - src/locale/pt-PT/ieview/contents.rdf - src/locale/ru-RU/ieview/contents.rdf - src/locale/sk-SK/ieview/contents.rdf - src/locale/sl-SI/ieview/contents.rdf - src/locale/sr-RS/ieview/contents.rdf - src/locale/sr-YU/ieview/contents.rdf - src/locale/sv-SE/ieview/contents.rdf - src/locale/th-TH/ieview/contents.rdf - src/locale/tr-TR/ieview/contents.rdf - src/locale/uk-UA/ieview/contents.rdf - src/locale/vi-VN/ieview/contents.rdf - src/locale/zh-CN/ieview/contents.rdf - src/locale/zh-TW/ieview/contents.rdf + src/old/Makefile + src/rebuild/Makefile m src/rebuild/chrome.manifest.in + src/rebuild/chrome/content/ieviewOverlay.js + src/rebuild/chrome/content/ieviewOverlay.xul + src/rebuild/chrome/content/ieviewsettings.xul m src/rebuild/chrome/content/reloaded.html + src/rebuild/chrome/locale/bg-BG/ieview.dtd + src/rebuild/chrome/locale/bg-BG/ieview.properties + src/rebuild/chrome/locale/ca-AD/ieview.dtd + src/rebuild/chrome/locale/ca-AD/ieview.properties + src/rebuild/chrome/locale/cs-CZ/ieview.dtd + src/rebuild/chrome/locale/cs-CZ/ieview.properties + src/rebuild/chrome/locale/da-DK/ieview.dtd + src/rebuild/chrome/locale/da-DK/ieview.properties + src/rebuild/chrome/locale/de-DE/ieview.dtd + src/rebuild/chrome/locale/de-DE/ieview.properties + src/rebuild/chrome/locale/el-GR/ieview.dtd + src/rebuild/chrome/locale/el-GR/ieview.properties + src/rebuild/chrome/locale/en-US/ieview.dtd + src/rebuild/chrome/locale/en-US/ieview.properties + src/rebuild/chrome/locale/es-AR/ieview.dtd + src/rebuild/chrome/locale/es-AR/ieview.properties + src/rebuild/chrome/locale/es-ES/ieview.dtd + src/rebuild/chrome/locale/es-ES/ieview.properties + src/rebuild/chrome/locale/eu-ES/ieview.dtd + src/rebuild/chrome/locale/eu-ES/ieview.properties + src/rebuild/chrome/locale/eu/ieview.dtd m src/rebuild/chrome/locale/eu/ieview.properties + src/rebuild/chrome/locale/fi-FI/ieview.dtd + src/rebuild/chrome/locale/fi-FI/ieview.properties + src/rebuild/chrome/locale/fr-FR/ieview.dtd + src/rebuild/chrome/locale/fr-FR/ieview.properties + src/rebuild/chrome/locale/fy-NL/ieview.dtd + src/rebuild/chrome/locale/fy-NL/ieview.properties + src/rebuild/chrome/locale/he-IL/ieview.dtd + src/rebuild/chrome/locale/he-IL/ieview.properties + src/rebuild/chrome/locale/hr-HR/ieview.dtd + src/rebuild/chrome/locale/hr-HR/ieview.properties + src/rebuild/chrome/locale/hu-HU/ieview.dtd + src/rebuild/chrome/locale/hu-HU/ieview.properties + src/rebuild/chrome/locale/it-IT/ieview.dtd + src/rebuild/chrome/locale/it-IT/ieview.properties + src/rebuild/chrome/locale/ja-JP/ieview.dtd + src/rebuild/chrome/locale/ja-JP/ieview.properties + src/rebuild/chrome/locale/km-KH/ieview.dtd + src/rebuild/chrome/locale/km-KH/ieview.properties + src/rebuild/chrome/locale/ko-KR/ieview.dtd + src/rebuild/chrome/locale/ko-KR/ieview.properties + src/rebuild/chrome/locale/lt-LT/ieview.dtd + src/rebuild/chrome/locale/lt-LT/ieview.properties + src/rebuild/chrome/locale/nb-NO/ieview.dtd + src/rebuild/chrome/locale/nb-NO/ieview.properties + src/rebuild/chrome/locale/nl-NL/ieview.dtd + src/rebuild/chrome/locale/nl-NL/ieview.properties + src/rebuild/chrome/locale/pl-PL/ieview.dtd + src/rebuild/chrome/locale/pl-PL/ieview.properties + src/rebuild/chrome/locale/pt-BR/ieview.dtd + src/rebuild/chrome/locale/pt-BR/ieview.properties + src/rebuild/chrome/locale/pt-PT/ieview.dtd + src/rebuild/chrome/locale/pt-PT/ieview.properties + src/rebuild/chrome/locale/ru-RU/ieview.dtd + src/rebuild/chrome/locale/ru-RU/ieview.properties + src/rebuild/chrome/locale/sk-SK/ieview.dtd + src/rebuild/chrome/locale/sk-SK/ieview.properties + src/rebuild/chrome/locale/sl-SI/ieview.dtd + src/rebuild/chrome/locale/sl-SI/ieview.properties + src/rebuild/chrome/locale/sr-RS/ieview.dtd + src/rebuild/chrome/locale/sr-RS/ieview.properties + src/rebuild/chrome/locale/sr-YU/ieview.dtd + src/rebuild/chrome/locale/sr-YU/ieview.properties + src/rebuild/chrome/locale/sv-SE/ieview.dtd + src/rebuild/chrome/locale/sv-SE/ieview.properties + src/rebuild/chrome/locale/th-TH/ieview.dtd + src/rebuild/chrome/locale/th-TH/ieview.properties + src/rebuild/chrome/locale/tr-TR/ieview.dtd + src/rebuild/chrome/locale/tr-TR/ieview.properties + src/rebuild/chrome/locale/uk-UA/ieview.dtd + src/rebuild/chrome/locale/uk-UA/ieview.properties + src/rebuild/chrome/locale/vi-VN/ieview.dtd + src/rebuild/chrome/locale/vi-VN/ieview.properties + src/rebuild/chrome/locale/zh-CN/ieview.dtd + src/rebuild/chrome/locale/zh-CN/ieview.properties + src/rebuild/chrome/locale/zh-TW/ieview.dtd + src/rebuild/chrome/locale/zh-TW/ieview.properties + src/rebuild/chrome/skin/ieview-button-16px-disabled.png + src/rebuild/chrome/skin/ieview-button-16px.png + src/rebuild/chrome/skin/ieview-button-24px-disabled.png + src/rebuild/chrome/skin/ieview-button-24px.png + src/rebuild/chrome/skin/ieview-icon.png + src/rebuild/chrome/skin/ieview-toolbar-button.css + src/rebuild/defaults/preferences/ieview.js m src/rebuild/inslocales.pl m src/rebuild/install.rdf.in m src/rebuild/makemanifest.pl
checkpoint on the way to a modernized extension layout
m src/content/reloaded.html
pass function object to setTimeout, re: #22
+ src/.gitignore
added .gitignore
m src/Makefile m src/content/contents.rdf m src/install.rdf.in
marked Firefox 10.* compatible, minor^3 version bump
m src/install.rdf.in
upped seamonkey maxVersion, added em:type
m src/Makefile m src/content/contents.rdf m src/content/reloaded.html m src/inslocales.pl m src/install.js.in m src/install.rdf.in
updated compatibility list, ie view home page
If more than two people ask me about something I'm using, especially if it's more than two people at one show, it seems worth the time to write it down.

It may look like my friend Brian is paying close attention, awaiting the moment when he'll add some more harmonies. Actually, he's looking past me, at my iPad, attached to a nearby stand.

It's held there by an IK Multimedia iKlip mic stand mount. There are a number of products that do this job; this is the one I happen to own, and it does its one job very well.
So why is it there? I know folks who run GarageBand onstage this way, but in my case, it's just lyric sheets and/or setlists. So when I need a memory jog on a new song, the occasional cover or (as sadly happened last night) when I blank on an older song of mine, it's right there.
There's any number of ways to get those lyrics on there, but like any good nerd, I lean toward plain text files. Something I can edit on any machine I own, view on my phone if need be, etc.
The app used for viewing the songs is GoodReader. I like that it will read almost anything I throw at it, but especially like the way it syncs with Dropbox. I have a "Lyrics" folder on my laptop:

and I've told GoodReader to sync that folder and its contents. If I add a new song to that folder, it will automatically be mirrored to GoodReader. Tap a song, and off I go:
If I'm feeling particularly grown-up and professional, I'll "star" some of the songs ahead of time, and let GoodReader show me just those songs as something approaching a setlist:

That's it. No more binder of lyrics for me, and I'm pretty happy about it. I do recommend putting the iPad in airplane mode before your set, to avoid the temptation to tweet mid-song.

I find this photo…just…dazzlingly disturbing.
Couldn’t even say why, but this feels like the visual equivalent of being told you’re eating human flesh.
It looks like a still frame from an old Python animation; I imagine Cryer continues to rise up, knocking Ashton’s head off, which then lands on what I choose to believe is the word “FAIL”.
Finishing up our Veracity module overview, let’s look at a few “fitting in” considerations.
If your server_files/ui/modules/yourmodule folder contains a menu.js file, that file will be loaded via a <script> tag in the footer of every Veracity web page.
Whatever you want to add/change in the Veracity menu, do it here. In our case (and most cases), we just append an item to the <ul> named topmenu, which is (surprise!) Veracity’s top-level menu:
var tm = $('#topmenu');
var mi = $("<li id='topmenuwiki'></li>");
var ln = $("<a class='menulink'>wiki</a>").
attr('href', sgCurrentRepoUrl + "/wiki.html").
appendTo(mi);
tm.append(mi);
Notice that you can count on jQuery being available to your code.
Wiki page changes show up in Veracity’s activity stream, alongside commits, bug updates, etc. The activity stream interface is a simple one: you need to create an object supporting three methods:
name(): returns a string describing this particular activity component, for debug logging. Totally up to you.dagsUsed(): returns an array of database DAG IDs, for caching. Include any DAGs your activity stream might query. In our case, it’s [sg.dagnum.WIKI, sg.dagnum.USERS]getActivity(): where all of the work happensgetActivity() returns an array of objects, with (at least) the following members:
what: A short description of the object that changed, updated, etc.title: Usually redundant to “what”. Used for Atom entry titles.action: What happened to that thing (created, updated, deleted, fixed…)who: The Veracity user ID to whom this activity should be attributed (a committer, the editor of this particular Wiki change, etc.)when: The (Unix timestamp) time when this activity occurred.link: Optional, a link to the object, its history, etc.Bug updates, for example, contain (among other things):
{
"what": "Work items that reference missing changesets can not be viewed",
"title: "Work items that reference missing changesets can not be viewed",
"action": "Fixed X1384",
"who":"g02d63075631e47bc8a29dad7027f59d382cff0ac413311e0838c60fb42f09aca",
"when":1313620280696.000000,
"link":"/workitem.html?recid=gdbb98600a5114533a0a936226f4b2efb8e381b80c91811e0b40f1c6f65d71da9"
}
You should return the most recent N items. The activity stream wrappers will sort them in with other activity sources before returning the JSON or Atom stream.
In the wiki’s case, we build records like so:
var record = {
what: thispage.title,
title: thispage.title,
who: thispage.userid,
when: thispage.timestamp
};
if (first)
record.action = "Created Wiki page";
else
record.action = "Edited Wiki page";
if (lastpage)
{
if (lastpage.title != thispage.title)
{
record.action = "Renamed Wiki page";
record.what += " (was " + lastpage.title + ")";
}
}
record.link = '/wiki.html?page=' + encodeURIComponent(title);
The first time a page is seen, we report it as “created”; thereafter, as “edited”. If the title changes along the way, we note that instead.

That’s about it, as module high points go. Further questions are very much welcome at the Veracity Q/A site.
So how does the Wiki module work, anyway? Pretty much the way you’d expect a web app to work.
All of the Veracity-specific stuff happens on the server side.
We retrieve the existing page in the GET
/wiki/pages/<pagename>.json route (mentioned in Part 2 the other day). Normally (q.v.), that’s as simple as:
var db = new zingdb(request.repo, sg.dagnum.WIKI);
var w = vv.where( { "title": request.pagename } );
var recs = db.query('page', ['text','title','recid'], w);
return( recs[0] );
Which translates to “Open the wiki database, find any records matching
our pagename, grab their text, title and recid fields, and
return the first one.” We can get away with this since our database
template requires the pagename to be unique.
A JSON representation of that object is returned (you’ll also see some caching logic in there, but that’s strictly a performance measure, ignorant of the Wiki data).
Updates work like so:
var csid = newrec._csid || null;
delete newrec._csid;
ztx = db.begin_tx(csid, request.headers.From);
if (newrec.recid)
rec = ztx.open_record('page', newrec.recid);
else
{
rec = ztx.new_record('page');
newrec.recid = rec.recid;
}
rec.title = newrec.title;
rec.text = newrec.text;
vv.txcommit(ztx);
We then return OK to our caller, the page is reloaded, the circle of
life continues.
So what’s up with the changeset ID, and why did we have to say “normally” before?
It’s possible that, by the time you’re saving your changes, someone else has updated the same page. Or maybe your changes are in a nice straight line locally, but a push or pull brings in someone else’s previously-unknown edits. Veracity doesn’t get to throw up its hands and fail. It needs to merge.
And to merge your changes and mine, it needs to know where we each started from. That’s why we pass the changeset IDs around; it tells Veracity “here’s my latest changes, and the last version I knew of was rev 1234”. Later, when Veracity merges that with someone else’s updates, it knows those were based on rev 1235; it finds a common ancestor, does a smart 3-way merge, and all’s well. Almost always.
“Almost always” is not “absolutely always”, though.
What if we both started with:
line 1
line 2
as our text. Then I edited it to read:
line 1
line one and a half
line 2
while off on your machine, you edited it to:
line 1
line 1.5
line 2
Then you pull my changes. Now what? Should your changes be thrown away? Should mine? Should both lines be included? Any of these are possible, but in the template we have to pick one.
The “merge strategy” the Wiki template uses is to concatenate our two texts, and let a human being sort things out. Elsewhere (e.g. in the scrum module) we use all sorts of other strategies, including automatically changing the ID of a work item when it conflicts with one created elsewhere). Since Wiki text is intended for human usage only, and is completely arbitrary, there’s no sense trying to guess the “appropriate” conflict resolution between two edits.
So in this situation, anyone opening the merged page will see:

Edit that as needed, and all’s well with the world again.
Next (and hopefully final) time: plugging into Veracity’s activity stream and cache.
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:
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:
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:
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:
"/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:
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.
Veracity was built to be extensible. Not just by virtue of the source being available (although that helps), but also via the embedded JavaScript interpreter. Without writing a line of C, new HTML pages and REST urls can be added, supporting entirely new data types and functionality.
The “native” Scrum functions in Veracity (work items, build tracking, time tracking, milestones, filters, etc.) are nowhere to be found in the Veracity library code. It’s Javascript all the way down, helped out by client-side scripts and HTML. As we add new (or alternative) features ourselves, expect to see many of them show up as new “modules” of pluggable (and un-pluggable) code.
We were going to be adding a Wiki module to Veracity eventually, anyway; and since we wanted to show you how modules are built, a simple Wiki seemed like a nice place to start. This isn’t production code (yet), but it’s an instructive start.
For today, let’s just get the Wiki installed in your Veracity server and make sure everything’s up and running.
Whether you’re building from source or running from a pre-built installer, you’ll need to download the latest nightly build (1.0.1.10527 or later) to retrieve the Wiki module. If you’ve cloned from our public repository, you’ll want to pull the latest.
Within your source folder or the unpacked archive, look for .../src/modules/wiki. Within that directory, you’ll see a README file, some license material, a test directory, and the part you actually care about: a server_files folder.
You’ll want to copy the contents of that folder into Veracity’s server files folder. If you’re unsure where that is, run vv config. Towards the end, you’ll see something like:
server/files: /home/alanswann/veracity/src/server_files
in this case, from within modules/wiki, you’d say (on Unix-y systems):
[~/veracity/src/modules/wiki]
$ cp -R server_files/* /home/alanswann/veracity/src/server_files
and your Wiki code should now included with the rest of Veracity.
[~/veracity/src/modules/wiki]
$ ls ~/veracity/src/server_files/modules
scrum wiki
Note the above-mentioned scrum module alongside the wiki.
Start (or restart) vv serve, and you should see a wiki item in the top menu.

Click that. You’ll see a default home page, explaining that, hey, it’s a default home page. You can edit this page (using Markdown), or click the new page link to add another page.
As pages are created, they’ll show up in the right sidebar, to be clicked and viewed, or inserted into another page’s edit box as links.

Next post, we’ll start looking at the code. The good news? All of the heavy lifting is done for us, from the flexibly-licensed editor and preview tools, to the Veracity libraries that handle saves, updates and merges (gotta have merges, it’s a distributed Wiki, after all). I just glued them together.
"Disappear", "One Man at Best", "The Great Unknown" and "Mind of Its Own", live at the Broward Center for the Performing Arts.
All four songs are from my album "Acrophobe", available for purchase (CD or download) at music.paulroub.com.
Had the pleasure, last Saturday, of joining Michael Stock in studio on his Folk and Acoustic Music show. This is one of the songs performed therein.
And... uh... sorry about the sunburn. It didn't look that bad on the radio.
"A Little Bit Wiser" is $0.89 at Amazon MP3 - proceeds will go towards sunscreen and common sense.
This may well be obvious to anyone bothering to read this blog, but it’s helpful enough for me that I thought I’d scribble it down, anyway.
You’ve made a minor change, just adding some validation code:
$ vv status
Modified: @/validate.c
$ vv diff
=== ================
=== Modified: File @/validate.c
--- @/validate.c 4b8c0a0278cc18fdeb8592a6b56b81ba4c4b6841
+++ @/validate.c 2011/07/11 19:41:01.000 +0000
@@ -4,4 +4,5 @@
void validate(const char *user)
{
printf("%s, you're awesome.\n", user);
+ printf("And attractive.\n");
}
You commit, then pull the latest from your team before pushing your changes up.
There are inded some changes, so you’ll need to merge:
$ vv heads
revision: 7:11e7676e2d8c96071d6ae6748afb29fbea291d3c
branch: master
who: otherguy@example.com
when: 2011/07/11 15:44:24.448 -0400
comment: more changes you don't care about
parent: 5:54675c9beeab003fce135282654cf36f9032f326
revision: 6:3926e0614eb2164bb0839eb1c6ba2c4954107dcf
branch: master
who: me@example.com
when: 2011/07/11 15:42:08.610 -0400
comment: additional validation
parent: 4:55205503daa35db9fd3699473da84f49493ef03c
$ vv merge
4 updated, 0 deleted, 3 added, 0 merged, 0 unresolved
$ vv status
Added: @/othercode.c
Added: @/othercode.h
Added: @/whatisthisidonteven.c
Modified: @/addsprint.js
Modified: @/connect.js
Modified: @/reqtest.js
Modified: @/validate.h
Hrm. A lot of code, none of it yours. The resulting diff is so long I won’t bother to fake it up for the blog. But are you sure none of it’s yours? Skimming through hundreds of lines of diff output looking for problem code is not fun. And all for your little one-liner.
So turn the merge on its head. Starting from the other side, you should easily be able to tell if your changes are being merged in a sane fashion.
# clean slate
$ vv revert --all
# start from the other guy's changes
$ vv update -r 7
# merge in yours
$ vv merge
1 updated, 0 deleted, 0 added, 0 merged, 0 unresolved
$ vv status
Modified: @/validate.c
$ vv diff
=== ================
=== Modified: File @/validate.c
--- @/validate.c 4b8c0a0278cc18fdeb8592a6b56b81ba4c4b6841
+++ @/validate.c 2011/07/11 19:46:21.000 +0000
@@ -4,4 +4,5 @@
void validate(const char *user)
{
printf("%s, you're awesome.\n", user);
+ printf("And attractive.\n");
}
# looks familiar. we're good.
$ vv commit -m"merge"
There's an excellent chance that, very soon, you won't be able to comment anonymously on openmikes.org listings.
"Report a Problem" will still work, as a way to tell me, privately, that something needs to be corrected. But public comments will require you to login; probably via Facebook, to make most people's live easier.
"Comment wars" on a few listings (almost always anonymous), filled with personal attacks, are a prime example of what happens when people don't have to take responsibility for their words. That, and one particular commenter who uses an endless succession of false names (I delete most of that before you folks need to bother with it), are motivating me to eliminate that option.
If you want to speak up against this change, now's the time.
Chipping away at my Veracity tasks today, I ran across an issue that’s common in DVCS + Centralized Bug Tracking scenarios, but unnecessary and easily-avoided in Veracity.
In a nutshell, it’s this:

Until that code is pushed to the shared server, QA will have my bug in their to-verify list, but won’t have the code to do so.
I might delay the push for a number of reasons - waiting to finish a couple of related bugs and push as a unit; needing to merge and test someone else’s latest code before pushing the results back; etc. Until I’m done, though, we’re out of sync.
You can keep track via special statuses, tags, stamps, etc. (and remember to reset them once you’ve pushed), but why?
When I’m working on Veracity, I’m usually working against a local instance of the bug tracking / web UI. It’s faster, I’m not competing for resources, and I get to do things like associate commits to bugs right from the command line while I work.
It also avoids the problem above, since my workflow looks like this:

This is normal in the Veracity world, and unremarkable except in those instances, like this afternoon, when someone marks a bug closed without pushing up the associated code. Not naming names. We all make mistakes, and I’m sure writing a book can be pretty distracting.
I’m a developer at SourceGear, working on Veracity these days.
Elsewhere in my life, there’s mainly music - you can find out more about that at paulroub.com.
My complaining-about-air-travel needs are handled on Twitter as @paulroub.