Zow, fabulous post. Lots of great stuff.
CommentsPlugin author mahemoff is on the list so perhaps he can use
this as a guide to fix that stuff.
Other comments from me within.
On Jul 23, 2009, at 2:32 PM, Oveek wrote:
> Been working on some cosmetic stuff, trying to put together a
> TiddlyWebWiki theme for our intranet. This has been sort of hindering
> my progress for a while. I've been wanting to create an easy to read,
> and fresh look, but there are so many page elements it's a bit painful
> tweaking everything.
I'm glad to hear this. One of the things that always falls off my list
of priorities is making stuff easy to read, fresh, etc.
> Theory on what's happening: In most cases the order of arguments to
> merge() doesn't matter too much, but it does here.
>
> * The first thing is that config.defaultCustomFields is a global
> object and persists through the life of an open TiddlyWiki.
> * The second point is that when a tiddler is saved its attributes get
> assigned by reference in Tiddler.prototype.assign().
>
> The result is that the field attribute of consecutive comments end up
> pointing to the same values / variable / memory location.
Good catch.
> - The other stuff is pretty much take it or leave it. I tried out a
> new comment naming scheme so you can look at the timeline and know
> what tiddler a comment was made on.
I struggle with the wiki notion of referring to anything by its name,
rather than some more persistent ID. It's a great and powerful thing,
up until the point when it is not...and then it's really not. So, for
example what happens to comments when the root tiddler is renamed.
> There are a couple problems with this scheme, long names being one.
> Some variation of this would might be better. The main motivation was
> to let users know what tiddler was commented on just by seeing the
> comment title.
That is valuable. On http://tiddlyweb.peermore.com/ the Timeline is
filled with comments that convey no meaning, just that lots of
comments have happened, and while you can click on the comment to see
its breadcrumb, because of the way navigation works in tiddlyweb, you
can scroll the timeline out of view when you click, so opening up all
the comments in quick succession to have a browse is not easy.
I think we're going to find that a lot of the TiddlyWiki UI aspects
that work great when there is only one author start to go a bit sour
when there are many.
> That's a 'sandbox' I put up on appspot to share TiddlyWebWiki stuff
> with everyone. By the way, I agree with a lot of Jeremy's comments in
> the other thread, and it would be great to have a TiddlyWebWikiSpot
> sometime. I know it's somewhere in the pipeline. I think it will give
> people an 'easy in' for playing with TiddlyWeb.
It should also be fairly straightforward to make it awesome.
Did you have [m]any issues with getting things running on GAE? I
haven't tried there lately; don't keep tiddlyweb.appspot.com up to date.
> ...I'm using the latest git master on appspot, and found a problem
> while setting it up. There's currently an ETag problem that occurs
> when updating existing tiddlers. The issue results from mismatched
> revisions.
Ah, yes. This is somewhat expected. Or rather doesn't surprise me. The
revision handling in ETags with non-revision-using stores is entirely
untested. If you can delineate some English simple tests that sort of
cover the problems you've encountered I'll add them as code to the
test suite.
> except StoreMethodNotImplemented:
> revision = 0
This seems like it is probably correct, it also points out that there
is some duplication in tiddler_put in the text store, which looks up
the revision on disk a second time (after the handler has already done
it). Either only the store should do it, or only calling code, not both.
> I don't know if these two changes break anything else, but they fix
> the etag conflicts and allow to create, save, and delete in the
> googldata store. I don't know if there's a less invasive way to fix
> this, but the change to tiddler.py seems unavoidable.
Have you had an opportunity to see if your changes continue to work
with a revision supporting store, such as the text store of sql store?
The thing I'm most unsure about is what the right fix is in the client
code. There it seems like the revision should always be 1 for a newly
created tiddler.
Indeed - thanks for your efforts!
> CommentsPlugin author mahemoff is on the list so perhaps he can use
> this as a guide to fix that stuff.
Mike is aware of your work and will likely respond soon.
> The thing I'm most unsure about is what the right fix is in the client
> code. There it seems like the revision should always be 1 for a newly
> created tiddler.
I agree. Not sure what the best way to resolve this is either.
The easy way out would be to say that TiddlyWebWiki expects a
revision-supporting store by default, with the option of having a
different TiddlyWebConfig set the base revision to 0 if required
(perhaps store properties* could be reported by the status plugin).
-- F.
* it gets more complicated with the multistore...
> I agree. Not sure what the best way to resolve this is either.
> The easy way out would be to say that TiddlyWebWiki expects a
> revision-supporting store by default, with the option of having a
> different TiddlyWebConfig set the base revision to 0 if required
> (perhaps store properties* could be reported by the status plugin).
Not keen on this: it doesn't resolve the problem, it just moves it
around. Having to query the server for store properties is like a big
design slap saying, "you've got a big problem with the genericity of
your system". So lets see if we can fix it for real.
I think the issue is the server side comprehension or handling of the
incoming tiddler. It should ignore the revision field of the incoming
tiddler when it is a strict PUT (i.e. not a POST of tiddler revision
history as done with a rename). If the revision field is set to None
before entering the store things should "just work".
This works out logically because we have already made a statement in
the design of the system that revisions, when they exist, are
immutable. Whenever we PUT a tiddler we are creating a new revision,
and it is up to the store to determine what the next revision id is.
I need to go back through Oveek's messages to understand the Etag
situation a bit better, I think there may be a logic flaw there, as
that's really the crux of the biscuit. My brain is not currently
warmed up enough...
In this context is the root tiddler, the tiddler which is being
commented upon? If so, that might be a non-starter. One of the
advantages of the CommentsPlugin as it stands right now is that it
works entirely from client code and requires no write permission on
the tiddlers being commented upon, just create permission in the
comment bag.
>> Did you have [m]any issues with getting things running on GAE? I
>> haven't tried there lately; don't keep tiddlyweb.appspot.com up to
>> date.
>
> Aside from the revision conflict everything worked right out of the
> box.
Remarkable! :)
> How hard would it be to add support for roles to the store? The
> sandbox is a little vulnerable right now without any access control.
Not too hard, but mildly complex:
* A User model would need to be added to the googledata store
* An interface of some sort that allowed for the assignment of roles
to google user names
* Extending the extractor so it looked up the current google user in
the User model to get their roles (if any)
> Yes, this is certainly something that needs work. For now, I've
> changed the title to include the name of the root tiddler - so it's of
> the format cakeComment98765 (where "cake" is the tiddler being
> commented on and "98765" is a GUID). I'd like to change the GUID
> component to reflect the position in the hierarchy, e.g. 1_1_3, but
> it's not trivial as there's the possibility of a conflict in the case
> of a TiddlyWeb-backed comment store (as opposed to a standalone
> TiddlyWiki). i.e. commenter A submits a new comment, commenter B
> already has the page open and submits a new comment - the client will
> be trying to submit it with an ID that's already being used. The way
> around this is to introduce a callback handler, which I think would
> work, but needs time to implement.
Encoding structuring information into the comment title seems like a
bad idea to me. In addition to clouding the presentation with extra
stuff, you're making the identifier of the comment have meaning, which
almost always leads to trouble down the road. Better, I think, to use
tiddler fields in the way you've mostly done it already making the
tree of comments traversable solely by data on the tiddlers themselves.
I tend to agree with Chris on this.
While it's probably worth having the root tiddler's title in the
comment's title (mostly due to the timeline situation*), I don't think
the hierarchy-dependent ID conveys enough useful information to make it
worth the trouble.
-- F.
* while I can see Oveek's point about separating ID and caption, it's a
can of worms that would require patching all over the place, including
third-party plugins and TiddlyWeb serializers
Absolutely.
My Cork experiment, for example, uses (pseudo-)UUIDs and a caption field.
However, retrofitting TiddlyWiki-based verticals that way would be hard.
(I realize TiddlyDocs is TiddlyWiki-based, but it's probably fairly
self-contained.)
-- F.
Normally, the ETag handling should prevent any clobbering and report an
edit conflict instead.
Having said that...
a) I don't know how well this works for revision-less stores (that's the
topic of the other branch of this thread, I suppose)
b) not sure whether Mike's CommentsPlugin supports ETags (presumably, it
just relies on the TiddlyWebAdaptor - at least in the TiddlyWiki
version)
c) there's currently a bug in the TiddlyWebAdaptor which affects
newly-created tiddlers:
http://trac.tiddlywiki.org/ticket/1112
> My preference [for comments] is that something, whatever it is, be
> something human readable and not something mysterious to the user.
I agree, there should be at least a hint somewhere. So some combination
of title + UUID seems like a good compromise - even at the risk of
renaming messing things up (which, in fact, probably is the case even
now when we're just using custom fields).
In fact, retaining the original tiddler title in the comment's title
could be considered the Right Way, as that was the context at the time.
-- F.
I've reviewed the revisions1 branch again (which is now just two
seemingly simple changes now) - but I'm not entirely confident
predicting whether it's correct or not.
Having said that, it seems to be doing the right thing, and testing it
against the text store revealed no issues.
Oveek, did you get a chance to test it against your revisionless store?
-- F.
> I've done some testing with the revisions1 branch against the
> googledata store and I'm having some etag conflicts. I initially
> reported some inconsistencies in the revision handling, and I still
> think that's the case. At the time I hadn't really pinned down what
> was going on, but I think I have a better idea of what's happening
> now. Still following up a couple loose ends; I'll check back shortly.
To add to the stack of available data about revisions handling I did a
little experimenting with the devtext store, which does not do
revisions. This is from the main code (release 0.9.55 in this case),
just to see what I could reproduce. I didn't really have any problems
with editing the same tiddler.
Also, under normal circumstances renaming a tiddler worked just fine,
as long as I was _only_ renaming. If I renamed a tiddler and also
edited its text, then I got an "Error saving f: OK" (f is the name of
the tiddler). Checking the tiddlyweb.log, no request is ever made to
the server, the error is purely on TiddlyWiki's side. If I then edit
the same tiddler, or any other tiddler, the tiddler is saved without
issue in the next sync back to server.
So I then confirmed the same issue was present with a store that does
revisions, and it is.
I'm using, as far as I know, the most recent TiddlyWebAdaptor etc, as
I'm working from a freshly created tiddlywebwiki instance.
So whatever issues might be present on the server side with revision
handling, there are also some client side issues that need to be
cleared up as well...
> I just tested revisions1 against the devtext store and also didn't
> have any problems.
Sigh. It looks like I had yet more unpushed code on one of my
machines. Yesterday it was in the tiddlywebwiki repo, today it was the
tiddlyweb revisions1 branch. This is what I get for switching to using
multiple machines.
In any case there's a chance our testing has been slightly different
as you revisions1 branch and my revisions1 branch are somewhat
different. What I've done is gone ahead and merged revisions1 to
master and pushed them both. If you could continue your testing from
the master branch we'll be on the same page.
I've just recreated my local app engine environment (c.f. above about
new machines) so can play long at home. I've just replicated this:
> One difference I see between running devtext and googledata is in the
> server.page.revision field. Under devtext, tiddlers have
> server.page.revision = 1 while under googledata, tiddlers have
> server.page.revision = 0.
And indeed that's not the desired behavior. I'll see where the fix
needs to happen.
> One difference I see between running devtext and googledata is in the
> server.page.revision field. Under devtext, tiddlers have
> server.page.revision = 1 while under googledata, tiddlers have
> server.page.revision = 0.
The proximal cause for this behavior is just simply that googledata
doesn't set the revision whereas devtext does, despite not supporting
revisions. This is because it subclasses the text store, and in code
in the super class revision does get set.
The following diff explicitly set the revision to 1. Making the store
responsible for the entire form of the tiddler is in line with how the
storage adaptors are theoretically supposed to work, but I'm not sure
this is the most elegant solution:
diff --git a/googleappengine/googledata.py b/googleappengine/
googledata.py
index 3d84cb2..40bd09b 100644
--- a/googleappengine/googledata.py
+++ b/googleappengine/googledata.py
@@ -152,6 +152,9 @@ class Store(StorageInterface):
if tiddler.type and tiddler.type != 'None':
tiddler.text = b64decode(tiddler.text.lstrip().rstrip())
+ # explicitly set the tiddler revision to 1 since we don't
support
+ # revisions.
+ tiddler.revision = 1
return tiddler
def tiddler_put(self, tiddler):
/me loves summaries
> * googledata store: Why do tiddler's have server.page.revision = 0
> when they are loaded from the googledata store? This is causing etag
> conflicts on tiddler edits. The devtext store, another revisonless
> store, doesn't seem to have this problem, why?
I think I've provided a moderately satisfactory fix for this in my
previous message. I'll go ahead and commit it.
> * another general etag issue: the TiddlyWebAdaptor does not generate
> etags unless a tiddler's workspace.type == "bag" (there's a comment
> alluding to this in adaptor.generateEtag()). However, newly created
> tiddlers have a workspace.type of "recipe". This means edits of newly
> created tiddlers never undergo an etag check on the serverside, or
> more accurately get an automatic pass because incoming_etag =
> "None" (you can verify this, and I can elaborate later).
FND, any comments?
> * sqlstore with postgres backend: Session handling / which
> transactions are not completing cleanly?
I'll look into this some. I suspect what's happening is that
exceptional conditions, such a tiddlers not being found and what not,
are raising exceptions that exit the core of the code before the
transactions are every resolved. I'll need to do some digging in the
sqlAlchemy docs to see the best way to handle this sort of thing.
> * Why unable to rename tiddlers that have been commented on?
Mike?
Ditto - thanks for that, Oveek!
Certainly helps tying up loose ends in this fairly complex thread.
>> * another general etag issue: the TiddlyWebAdaptor does not generate
>> etags unless a tiddler's workspace.type == "bag" [...]
>
> FND, any comments?
Yeah, I suspected this would come up sooner or later; back when that
adaptor was written, we didn't have enough solid use cases yet to decide
what should happen. Seems the situation is different now, which is good.
#1112* is already on my list for next week, so hopefully we can fix this
soon.
-- F.
> I think the approach looks quite clean and solid. Using the decorator
> centralizes the session handling in a single function, and in the
> given example also handles the case of nested transactions.
It does look good. Using a decorator is a smart way to handle this
sort of stuff.
Where TiddlyWeb differs is that (from the posting) this:
The common example is a web framework where the above framing
takes place in the BaseController class.
doesn't really apply in the adaptation style of persistence that
TiddlyWeb does. The gap between model and controller is
(intentionally) far wider than it is in the usual web framework.
I don't think that presents a problem in this case, it just moves the
locus of the decoration somewhat.
I think what can be done is that the decorator goes in sql.py and
their public methods get decorated.
> The example defines a decorator called @with_session(), which wraps
> all methods that do session stuff. So in our case we would move the
> self.session.commit() calls out of the methods and into the decorator.
> The example decorator also does rollbacks when there are exceptions--
> something we may need as well.
In the example decorator all exceptions are caught and the rollback
happens. We'll need to reraise so that things like NoBagError make it
upstream.
> Anyway take a look at that post and see what you think, it's quite
> illuminating and provides another potential approach that would let us
> keep using transactions (as
> opposed to using autocommit).
Is a good find.