Background
I’m still working on the bible wikis discussed in several previous threads. I’m trying to figure out an extension mechanism. The wikis themselves are supposed to be bare-bones. I’m hoping to find a way to make it easy to write extensions which can be enhanced with some simple language packs. And I’d love some advice.
The goal is that a user can write an extension: imagine an annotation tool, a prev/next navigation widget, a favorite verses tracker, a chapter-of-the-day reading list, or a random passage selector. And then this extension (plus perhaps a language pack) could be installed in any of these similarly-structured wikis.
Current extension
For this discussion, I’ll use a simple extension that adds information about the books. The basic wikis have just a flat list of books. But traditionally, the books are broken down into Old and New Testaments, which are further broken down groups such as Law
, History
, Poetry
, Minor Prophets
, Gospels
, Epistles
, etc. This extension1 would add such information to the wiki. Let’s assume that we want a new top-level entry in our default TableOfContents sidebar tab, called (in English) “Sections”, with Old Testament and New Testament underneath it, and Law
, History
, Gospels
and the like under those, and the books themselves categorized one level further down. The “In English” there is important. I want this to allow for simple language packs, in this case, for "Section/s"
, "Old Testament"
, "Major Prophet/s"
, "Gospel/s"
, and the like. (Note that the main wiki already has a language mechanism, covering among other things, the names of the biblical books. The extension can just use that.)
You can see this output in a demo version of the ongoing work. It looks like this:
Design Goals
While an extension may well add many new tiddlers, new templates, new stylesheets, and so forth, it may also add/remove tags or other fields from existing tiddlers or delete them altogether. I would prefer to be able to do this declaratively. I want the extension developer to be able to include something that doesn’t try to directly use Actions or Buttons, but simply says the user wants to:
- remove the tag “TableOfContents” from tiddler “Book”
- add tiddler “Section”, with caption “Sections” and the tag “TableOfContents”
- add tiddler “New Testament”, with tag “Section”
- add tiddler “Gospel” with caption “Gospels” and the tag “[[New Testament]]”
- add tag “Gospel” to the existing tiddler “Matthew”
- add tag “Gospel” to the existing tiddler “Mark”
…
And the language pack loaded for this library would override such text as “Section”, and “New Testament”, while the overrides for “Matthew” and “Mark” would come from the $:/language/*
tiddlers loaded with the language of their wiki.
I’m also hoping such a configuration would be reversible, so parallel to the install
button, there would also be an uninstall
one. This is simple enough for those things that add information: delete the new tiddler, remove the new tag. But I would have to introduce some cache for those things that delete or overwrite information. That’s not impossible, but it’s complex and I will probably simply ignore that for the moment.
Potential Mechanism
JSON
I started attempting this with JSON that looks like this:
{
"title": "Section",
"tags": "TableOfContents",
"caption": "Sections",
"_children": [
{
"title": "Old Testament",
"_children": [{
"title": "Law",
"_children": ["Genesis", "Exodus", "Leviticus", "Numbers", "Deuteronomy"]
},
{
"title": "History (OT)",
"caption": "History",
"_children": ["Joshua", "Judges", "Ruth", "1 Samuel", "2 Samuel", "1 Kings",
"2 Kings", "1 Chronicles", "2 Chronicles", "Ezra", "Nehemiah", "Esther"]
},
/* ... */
]
},
{
"title": "New Testament",
"_children": [/* ... */]
}
]
}
Full File
{
“title”: “Section”,
“tags”: “TableOfContents”,
“caption”: “Sections”,
“_children”: [
{
“title”: “Old Testament”,
“_children”: [
{
“title”: “Law”,
“_children”: [“Genesis”, “Exodus”, “Leviticus”, “Numbers”, “Deuteronomy”]
},
{
“title”: “History (OT)”,
“caption”: “History”,
“_children”: [
“Joshua”, “Judges”, “Ruth”, “1 Samuel”, “2 Samuel”, “1 Kings”,
“2 Kings”, “1 Chronicles”, “2 Chronicles”, “Ezra”, “Nehemiah”, “Esther”
]
},
{
“title”: “Wisdom”,
“_children”: [“Job”, “Psalms”, “Proverbs”, “Ecclesiastes”, “Song of Solomon”]
},
{
“title”: “Major Prophet”,
“caption”: “Major Prophets”,
“_children”: [“Isaiah”, “Jeremiah”, “Lamentations”, “Ezekiel”, “Daniel”]
},
{
“title”: “Minor Prophet”,
“caption”: “Minor Prophets”,
“_children”: [
“Hosea”, “Joel”, “Amos”, “Obadiah”, “Jonah”, “Micah”, “Nahum”,
“Habakkuk”, “Zephaniah”, “Haggai”, “Zechariah”, “Malachi”
]
}
]
},
{
“title”: “New Testament”,
“_children”: [
{
“title”: “Gospel”,
“caption”: “Gospels”,
“_children”: [“Matthew”, “Mark”, “Luke”, “John”]
},
{
“title”: “History (NT)”,
“caption”: “History”,
“_children”: [“Acts”]
},
{
“title”: “Epistle”,
“caption”: “Epistles”,
“_children”: [
“Romans”, “1 Corinthians”, “2 Corinthians”, “Galatians”,
“Ephesians”, “Philippians”, “Colossians”, “1 Thessalonians”,
“2 Thessalonians”, “1 Timothy”, “2 Timothy”, “Titus”,
“Philemon”, “Hebrews”, “James”, “1 Peter”, “2 Peter”, “1 John”,
“2 John”, “3 John”, “Jude”
]
},
{
“title”: “Prophecy”,
“_children”: [“Revelations”]
}
]
}
]
}
I had two problems with this approach. First, my JSON-in-TW skills are mediocre, and I was having difficulty looping over array indices with <$list>
widgets. I’m sure I could eventually figure that out, but second, I was not happy with the built-in assumption that Old Testament
, nested this way inside Section
would automatically get a tag of "Section"
.
As I tried to untangle that, I ended up considering a second approach:
Structured Field-Names
Thinking more on the lines of the declarative list above, I thought about something more like this, a tiddler with a number of fields:
section.title: Section
section.tags: TableOfContents
section.caption: Sections
old-testament.title: Old Testament
old-testament.tags: Section
law.title: Law
law.tags: [[Old Testament]]
Genesis.tags+: Law
Exodus.tags+: Law
Leviticus.tags+: Law
Numbers.tags+: Law
Deuteronomy.tags+: Law
history-ot.title: History (OT)
history-ot.caption: History
history-ot.tags: [[Old Testament]]
Joshua.tags+: [[History (OT)]]
Judges.tags+: [[History (OT)]]
Ruth.tags+: [[History (OT)]]
1 Samuel.tags+: [[History (OT)]]
2 Samuel.tags+: [[History (OT)]]
...
Full File
title: Sections Config
tags: TiddlerActions
section.title: Section
section.tags: TableOfContents
section.caption: Sections
old-testament.title: Old Testament
old-testament.tags: Section
law.title: Law
law.tags: [[Old Testament]]
Genesis.tags+: Law
Exodus.tags+: Law
Leviticus.tags+: Law
Numbers.tags+: Law
Deuteronomy.tags+: Law
history-ot.title: History (OT)
history-ot.caption: History
history-ot.tags: [[Old Testament]]
Joshua.tags+: [[History (OT)]]
Judges.tags+: [[History (OT)]]
Ruth.tags+: [[History (OT)]]
1 Samuel.tags+: [[History (OT)]]
2 Samuel.tags+: [[History (OT)]]
1 Kings.tags+: [[History (OT)]]
2 Kings.tags+: [[History (OT)]]
1 Chronicles.tags+: [[History (OT)]]
2 Chronicles.tags+: [[History (OT)]]
Ezra.tags+: [[History (OT)]]
Nehemiah.tags+: [[History (OT)]]
Esther.tags+: [[History (OT)]]
wisdom.title: Wisdom
wisdom.tags: [[Old Testament]]
Job.tags+: Widsdom
Psalms.tags+: Widsdom
Proverbs.tags+: Widsdom
Ecclesiastes.tags+: Widsdom
Song of Solomon.tags+: Widsdom
major-prophets.title: [[Major Prophets]]
major-prophets.tags: [[Old Testament]]
Isaiah.tags+: [[Major Prophets]]
Jeremiah.tags+: [[Major Prophets]]
Lamentations.tags+: [[Major Prophets]]
Ezekiel.tags+: [[Major Prophets]]
Daniel.tags+: [[Major Prophets]]
minor-prophets.title: Minor Prophets
minor-prophets.tags: [[Old Testament]]
Hosea.tags+: [[Minor Prophets]]
Joel.tags+: [[Minor Prophets]]
Amos.tags+: [[Minor Prophets]]
Obadiah.tags+: [[Minor Prophets]]
Jonah.tags+: [[Minor Prophets]]
Micah.tags+: [[Minor Prophets]]
Nahum.tags+: [[Minor Prophets]]
Habakkuk.tags+: [[Minor Prophets]]
Zephaniah.tags+: [[Minor Prophets]]
Haggai.tags+: [[Minor Prophets]]
Zechariah.tags+: [[Minor Prophets]]
Malachi.tags+: [[Minor Prophets]]
new-testament.title: New Testament
new-testament.tags: Section
gospels.title: Gospel
gospels.caption: Gospels
gospels.tags: [[New Testament]]
Matthew.tags+: [[Gospel]]
Mark.tags+: [[Gospel]]
Luke.tags+: [[Gospel]]
John.tags+: [[Gospel]]
history-nt.title: History (NT)
history-nt.caption: History
history-nt.tags: [[New Testament]]
Acts.tags+: [[History (NT)]]
epistles.title: Epistle
epistles.caption: Epistles
epistles.tags: [[New Testament]]
Romans.tags+: Epistle
1 Corinthians.tags+: Epistle
2 Corinthians.tags+: Epistle
Galatians.tags+: Epistle
Ephesians.tags+: Epistle
Philippians.tags+: Epistle
Colossians.tags+: Epistle
1 Thessalonians.tags+: Epistle
2 Thessalonians.tags+: Epistle
1 Timothy.tags+: Epistle
2 Timothy.tags+: Epistle
Titus.tags+: Epistle
Philemon.tags+: Epistle
Hebrews.tags+: Epistle
James.tags+: Epistle
1 Peter.tags+: Epistle
2 Peter.tags+: Epistle
1 John.tags+: Epistle
2 John.tags+: Epistle
3 John.tags+: Epistle
Jude.tags+: Epistle
prophecy.title: Prophecy
prophecy.caption: Prophecy
prophecy.tags: [[New Testament]]
Revelations.tags+: Prophecy
and then some code that is supplied this tiddler collects all this information. For instance:
section.title: Section
section.tags: TableOfContents
section.caption: Sections
would be gathered together by their shared “section” prefix, and because of the existence of .title
would be a new tiddler, with the given tags
and caption
. (Those prefixes are arbitrary, they could just as easily be numbers.)
And a line like this:
Joshua.tags+: [[History (OT)]]
would simply add a tag to the existing 'Joshua'
tiddler. I would presumably allow a tags-
suffix as well to remove one, I might also make the new tiddler signal more like section.title+
, as it would make the parallel removal, section.title-
more obvious and more consistent. I have three initial concerns over this. First, the data is somewhat more repetitive than the JSON version, which I never appreciate. Second, I’ve generally tried to be very conservative in my field-naming convention, generally preferring lower-case letters and hyphens only. This would add spaces, full stops, and plus signs as well. I don’t think that’s a problem, but I know there used to be issues with this, and I worry. Third, this is squeezing structured data into a flat format; it may simply feel awkward. None of these concerns are overwhelming, though.
I don’t know how to do all of this yet, but I don’t see any roadblocks. I’d likely do a POC in JavaScript first, then consider a wikitext version. But I’ve come up with one more interesting possible mechanism.
Bundle the tiddler deltas into a CompoundTiddler
A third possibility would be to use actual tiddlers with all the change information, stored together in a CompoundTiddler, and when this is consumed, new tiddlers are cloned into the main wiki, and other have their fields updated base on this information.
I don’t have any mockups of this, and haven’t thought it through as carefully. Moreover, I’m not even sure if it’s possible. Another recent topic asks about that. If it is possible, though, it would be the solution perhaps easiest for someone familiar with TiddlyWiki to use.
Question
Does any one of these seem like a better design? Does one of them feel more in-tune with TiddlyWiki? Is there another technique that I should be considering? Any other suggestions?
1 I’m using “extension” and not “plugin” because this probably can’t be comfortably done via the plugin mechanism. These tools will add tiddlers that should not be shadows; they will alter existing tiddlers to, say, add or remove tags.