teratorn
08-15-2008, 04:16 PM
Here is a first draft at design notes for a Plugin / Extension system for Ultra
Recall. This is just technical, but there should also be thought put in to an
Online Directory, Forums, and Rating Systems for Scripts (produced by Kinook or
by 3rd parties).
You might want to allow automated script installation... Basically what you do
is just register a new File Extension and Mime Type in Windows, and then
provide download links to files that are served with that extension and mime
type. They then are automatically opened by UR (and after checking the
CRYPTOGRAPHIC SIGNATURE, they are allowed to be installed).
Otherwise it will show a Big Bad Warning before allowing the installation of
untrusted plugins.
So anyway... Here goes:
Do *not* try to be language agnostic. Pick the language that you want to use,
and then USE IT FULLY. Don't artificially handicap any of the power that the
language, and the STANDARD LIBRARY OF THAT LANGUAGE (this is key!) has to
offer.
-= DATABASE RELATED APIs =-
DB QUERY API. There should be an API that allows the user to QUERY the database
for sequences of ITEMS, just in the same way that the Advanced Search dialog
box allows. DO NOT PROVIDE DIRECT ACCESS TO SQL. You don't want to restrict
your freedom to change the DB schema in the future, and we want scripts to be
forward-compatible as much as possible.
Items should be returned as objects that have members that represent the
fields. Or perhaps there will simply be one member (e.g. "fields") that is a
Dictionary (associative array) that maps Field Names to Field Values. Field
Vales should be Strong Typed, meaning a Date field ought to have some
language-specific DateTime object that represents those values.
To change DB VALUES you then will either directly MUTATE (modify) a Value
Object that you already hold a reference to, OR (In the case of Immutable Value
Types) you create a new value, and insert it on the Item Object.
TRANSACTION SUPPORT. Provide a simple wrapper for the SQLite transaction
START/END calls, so that scripts can do their work inside nice transactions,
yay!
(Advanced Feature) SINGLE-COLUMN, or MULTI-COLUMN QUERY API. Provide an Query
API that allows the programmer to pull only certain columns out of the DB.
Say you only can what the Flag value is for the items that match your query...
The result of the query would then NOT be a sequence of Object Items, but
rather a sequence Flag Values (of whatever type the Flag is represented by).
If you specify more than one column, then the result would be a sequence of
sequences (tuples), with each sub-sequence containing the Values of the Columns
for that particular Item.
(Advanced Feature) LAZY QUERY ITERATION. Instead of returning full in-memory
sequences, you can instead return Iterable Objects (how you do this depends on
the language being used, of course).
ITEM ID QUERY. Every item in the database should have a unique, global ID (in a
particular .urd file) that can be used to retrieve it.
I'm borrowing a lot of these concepts from the excellent Axiom database
available for Python (it uses SQLite v. 3 under the hood)
-= THREADING CONCERNS =-
Without looking at your codebase I can't know what kind of threading model you
utilize in UltraRecall. So it's pretty hard for me to say what kind of concerns
you will have to deal with in keeping scripts Thread Safe.
My basic thoughts are that all Scripting APIs (where ever possible) should do
locking implicitly and automatically where needed. Script Programs should be
allowed to spawn multiple threads, because sometimes you have to use threads to
do certain things.
However, I don't personally like writing multi-threaded programs very much.
It's extremely difficult to do it well, and to not introduce horrible,
impossible to debug errors in to your programs.
That said, I would lean towards providing an asynchronous Reactor pattern
implementation (event loop) that operates inside a single thread, and that
multiple independent Scripts can use at the same time. This is the same
event-driven model used in GUIs.
If you use Python as the extension language, then Twisted is the obvious choice
for this. You don't have to do any work here, and you give access to a
tremendous amount of power. (I'm a committer on Twisted, so I'm biased, but it
really is the best... :)
-= DATA MODEL =-
So, you ask, how do you represent the wide variety of content that UR can
handle? Good question.
I don't know.
Lets take Rich Text as an example. I have no idea how you represent Item
Content internally (as a Microsoft RTF document???), so I'm not sure what you
will have to do to expose that content in a programmable fashion.
Ideally, Scripts would be able to manipulate Rich Content in all of its detail.
You may need to invent an XML Schema here, that you can map your internal
representation on to. (X)HTML, anyone?
Or if you could somehow use MS Word internally, then you can use the Word 2003
XML format, while though it is ugly, it is certainly usable and extremely
feature-rich.
So if you use XML, or something else, you will need to provide a DOM API,
similar in nature to the DOM APIs provided in Javascript and by various XML
parsers.
If you use XML, then you can just grab one of the open source DOMs for your
language of choice.
Now, of course, The Tree in UltraRecall is fundamentally a DOM, and you may
wish to provide some integration here, and use the same API for accessing the
ULTRA RECALL DOM, and the various CONTENT DOMs.
Maybe it call all be part of a single DOM (one big tree)?
I'm just thinking out loud here.. and without knowing anything about the
codebase I'm not sure what the best way forward would be...
The DOMs I'm talking about above are fundamentally TEXT BASED. But that may not
be entirely suitable for UR. You may want to provide a DOM that contains not
just generic NODES, but Strongly Typed Objects (that can contain Strongly Typed
Children Objects and Strongly Typed Attributes).
-= GUI ACCESS =-
So, scripts obviously need to be able to perform any actions that the user can
perform through using the various menu items, shortcut keys, and buttons that
are in the GUI. The script should also have access to the various Controls that
fundamentally make up the UR GUI.
E.g. The Script API should allow access to the various Panes, and it should be
able to e.g. Make them wider.
I don't talk here about *modifiying* anything that is *shown* in a pane,
because that fundamentally belongs to the DB / DATA MODEL APIs. There should
obviously be an API to read which Item is currently Selected in the Current
Tab. And thus you can change the data on that Item, and it will reflect in the
various Panes.
An API to add new Tabs, enumerate and remove existing Tabs, and so on.
Access to the clipboard ( though you could just let people use native OS-level
access to the clipboard) instead of providing your own Scripting API for it.
And so on...
How hard all this is, and how it may be done of course depends on how you have
written your GUI layers. Is it just straight MFC/win32 API?
Scripts also need to be able to create their own GUI Controls and have them be
integrated with the Standard Controls where needed. E.g. A Script should be
able to provide a new toolbar, and you could then place, and customize that
toolbar like any of the built-in ones.
Scripts also need to be able to create their own Form Dialogs and Popup
Windows, etc.
You don't really need to do anything special here except choose a language that
is powerful enough to access Windows APIs, and then Scripts can do anything
they need to do.
Some thought will need to go in to how, and at what level, GUI integration is
allowed between Controls created by the Script and those created by UR.
-= NETWORKING =-
Network access is huge. E.g. I want a Plugin that lets be enter a Movie Name (
I have a database of all the DVDs in my collection), and it then pulls down all
the metadata for that movie from IMDB in to the new Movie Item that I am
inserting.
Or I might write a plugin that syncs my Contacts with Gmail.
As above, just give access to a language that already has all this. (Twisted is
the most powerful way to do networking under Python, bar none :)
-= DEPENDENCIES =-
There ought to be some simple mechanism that one Script can use to tell UR that
it depends on another script. E.g. The script GmailSync might depend on
MetaScript >= 2.0. MetaScript could be a Script that provides various library
routines that are useful to multipe Scripts, and it may provide some mechanisms
to allow inter-script communication (over loopback sockets or Windows Pipes,
etc). Or it could also provide a simple mechanism for a given Script to
enumerate other loaded Scripts and get references to their public objects,
allowing direct communication and collaboration between scripts.
-= CONCLUSION =-
So basically I want to be able to do EVERYTHING. But I know that I won't get
that in the end... That's OK. Even a small fraction of these things, exposed
through a powerful language, would be very useful.
The word "Script" is kind of misleading... I probably should have used the word
Plugin or Extension above.
The very first thing you guys could do would be to allow direct SQLite access
to ENCRYPTED database files. There are plenty of things I could do on my own if
I just had that. Of course it's not pretty, and it's not very safe, and it's
not gonna be forward-compatible. But I'll take it!
I'll take, and be grateful for (and pay for!) anything you give us. But of
course, the more the better!
P.S. Thanks for taking the time to read this. I spent quite a lot of time
thinking and writing, however it is still only a first draft, and I look
forward to constructive comments.
Recall. This is just technical, but there should also be thought put in to an
Online Directory, Forums, and Rating Systems for Scripts (produced by Kinook or
by 3rd parties).
You might want to allow automated script installation... Basically what you do
is just register a new File Extension and Mime Type in Windows, and then
provide download links to files that are served with that extension and mime
type. They then are automatically opened by UR (and after checking the
CRYPTOGRAPHIC SIGNATURE, they are allowed to be installed).
Otherwise it will show a Big Bad Warning before allowing the installation of
untrusted plugins.
So anyway... Here goes:
Do *not* try to be language agnostic. Pick the language that you want to use,
and then USE IT FULLY. Don't artificially handicap any of the power that the
language, and the STANDARD LIBRARY OF THAT LANGUAGE (this is key!) has to
offer.
-= DATABASE RELATED APIs =-
DB QUERY API. There should be an API that allows the user to QUERY the database
for sequences of ITEMS, just in the same way that the Advanced Search dialog
box allows. DO NOT PROVIDE DIRECT ACCESS TO SQL. You don't want to restrict
your freedom to change the DB schema in the future, and we want scripts to be
forward-compatible as much as possible.
Items should be returned as objects that have members that represent the
fields. Or perhaps there will simply be one member (e.g. "fields") that is a
Dictionary (associative array) that maps Field Names to Field Values. Field
Vales should be Strong Typed, meaning a Date field ought to have some
language-specific DateTime object that represents those values.
To change DB VALUES you then will either directly MUTATE (modify) a Value
Object that you already hold a reference to, OR (In the case of Immutable Value
Types) you create a new value, and insert it on the Item Object.
TRANSACTION SUPPORT. Provide a simple wrapper for the SQLite transaction
START/END calls, so that scripts can do their work inside nice transactions,
yay!
(Advanced Feature) SINGLE-COLUMN, or MULTI-COLUMN QUERY API. Provide an Query
API that allows the programmer to pull only certain columns out of the DB.
Say you only can what the Flag value is for the items that match your query...
The result of the query would then NOT be a sequence of Object Items, but
rather a sequence Flag Values (of whatever type the Flag is represented by).
If you specify more than one column, then the result would be a sequence of
sequences (tuples), with each sub-sequence containing the Values of the Columns
for that particular Item.
(Advanced Feature) LAZY QUERY ITERATION. Instead of returning full in-memory
sequences, you can instead return Iterable Objects (how you do this depends on
the language being used, of course).
ITEM ID QUERY. Every item in the database should have a unique, global ID (in a
particular .urd file) that can be used to retrieve it.
I'm borrowing a lot of these concepts from the excellent Axiom database
available for Python (it uses SQLite v. 3 under the hood)
-= THREADING CONCERNS =-
Without looking at your codebase I can't know what kind of threading model you
utilize in UltraRecall. So it's pretty hard for me to say what kind of concerns
you will have to deal with in keeping scripts Thread Safe.
My basic thoughts are that all Scripting APIs (where ever possible) should do
locking implicitly and automatically where needed. Script Programs should be
allowed to spawn multiple threads, because sometimes you have to use threads to
do certain things.
However, I don't personally like writing multi-threaded programs very much.
It's extremely difficult to do it well, and to not introduce horrible,
impossible to debug errors in to your programs.
That said, I would lean towards providing an asynchronous Reactor pattern
implementation (event loop) that operates inside a single thread, and that
multiple independent Scripts can use at the same time. This is the same
event-driven model used in GUIs.
If you use Python as the extension language, then Twisted is the obvious choice
for this. You don't have to do any work here, and you give access to a
tremendous amount of power. (I'm a committer on Twisted, so I'm biased, but it
really is the best... :)
-= DATA MODEL =-
So, you ask, how do you represent the wide variety of content that UR can
handle? Good question.
I don't know.
Lets take Rich Text as an example. I have no idea how you represent Item
Content internally (as a Microsoft RTF document???), so I'm not sure what you
will have to do to expose that content in a programmable fashion.
Ideally, Scripts would be able to manipulate Rich Content in all of its detail.
You may need to invent an XML Schema here, that you can map your internal
representation on to. (X)HTML, anyone?
Or if you could somehow use MS Word internally, then you can use the Word 2003
XML format, while though it is ugly, it is certainly usable and extremely
feature-rich.
So if you use XML, or something else, you will need to provide a DOM API,
similar in nature to the DOM APIs provided in Javascript and by various XML
parsers.
If you use XML, then you can just grab one of the open source DOMs for your
language of choice.
Now, of course, The Tree in UltraRecall is fundamentally a DOM, and you may
wish to provide some integration here, and use the same API for accessing the
ULTRA RECALL DOM, and the various CONTENT DOMs.
Maybe it call all be part of a single DOM (one big tree)?
I'm just thinking out loud here.. and without knowing anything about the
codebase I'm not sure what the best way forward would be...
The DOMs I'm talking about above are fundamentally TEXT BASED. But that may not
be entirely suitable for UR. You may want to provide a DOM that contains not
just generic NODES, but Strongly Typed Objects (that can contain Strongly Typed
Children Objects and Strongly Typed Attributes).
-= GUI ACCESS =-
So, scripts obviously need to be able to perform any actions that the user can
perform through using the various menu items, shortcut keys, and buttons that
are in the GUI. The script should also have access to the various Controls that
fundamentally make up the UR GUI.
E.g. The Script API should allow access to the various Panes, and it should be
able to e.g. Make them wider.
I don't talk here about *modifiying* anything that is *shown* in a pane,
because that fundamentally belongs to the DB / DATA MODEL APIs. There should
obviously be an API to read which Item is currently Selected in the Current
Tab. And thus you can change the data on that Item, and it will reflect in the
various Panes.
An API to add new Tabs, enumerate and remove existing Tabs, and so on.
Access to the clipboard ( though you could just let people use native OS-level
access to the clipboard) instead of providing your own Scripting API for it.
And so on...
How hard all this is, and how it may be done of course depends on how you have
written your GUI layers. Is it just straight MFC/win32 API?
Scripts also need to be able to create their own GUI Controls and have them be
integrated with the Standard Controls where needed. E.g. A Script should be
able to provide a new toolbar, and you could then place, and customize that
toolbar like any of the built-in ones.
Scripts also need to be able to create their own Form Dialogs and Popup
Windows, etc.
You don't really need to do anything special here except choose a language that
is powerful enough to access Windows APIs, and then Scripts can do anything
they need to do.
Some thought will need to go in to how, and at what level, GUI integration is
allowed between Controls created by the Script and those created by UR.
-= NETWORKING =-
Network access is huge. E.g. I want a Plugin that lets be enter a Movie Name (
I have a database of all the DVDs in my collection), and it then pulls down all
the metadata for that movie from IMDB in to the new Movie Item that I am
inserting.
Or I might write a plugin that syncs my Contacts with Gmail.
As above, just give access to a language that already has all this. (Twisted is
the most powerful way to do networking under Python, bar none :)
-= DEPENDENCIES =-
There ought to be some simple mechanism that one Script can use to tell UR that
it depends on another script. E.g. The script GmailSync might depend on
MetaScript >= 2.0. MetaScript could be a Script that provides various library
routines that are useful to multipe Scripts, and it may provide some mechanisms
to allow inter-script communication (over loopback sockets or Windows Pipes,
etc). Or it could also provide a simple mechanism for a given Script to
enumerate other loaded Scripts and get references to their public objects,
allowing direct communication and collaboration between scripts.
-= CONCLUSION =-
So basically I want to be able to do EVERYTHING. But I know that I won't get
that in the end... That's OK. Even a small fraction of these things, exposed
through a powerful language, would be very useful.
The word "Script" is kind of misleading... I probably should have used the word
Plugin or Extension above.
The very first thing you guys could do would be to allow direct SQLite access
to ENCRYPTED database files. There are plenty of things I could do on my own if
I just had that. Of course it's not pretty, and it's not very safe, and it's
not gonna be forward-compatible. But I'll take it!
I'll take, and be grateful for (and pay for!) anything you give us. But of
course, the more the better!
P.S. Thanks for taking the time to read this. I spent quite a lot of time
thinking and writing, however it is still only a first draft, and I look
forward to constructive comments.