Moo/Diary
6 July 2005
I went to take a look at InfiniteMonkey. Funny how you can miss totally obvious features, eg RSS output. This really amounts to a totally different output channel for the script. Then I read the bit about working with Wiki Clients, which adds a potential third channel.
So... do I add a level of abstraction in my Page hierarchy, like this:
Page +- HtmlPage +- WikiHtmlPage +- usual classes
Or do I introduce Yet Another orthogonal class system of "Output modules" that provide wrapping to the Wiki output? These would be HTML and RSS initially, and WikiClient could be a plug-in later.
My gut instinct it to lean towards the 2nd option, even though it's a big change in how the script works and it's also a whole new class tree to think about.
HtmlPage class
Pro:
- Relatively simple
- RssPage will have different children to a certain extent. Rss won't need things like a Diff page, or an Edit page and so on. So having a new branch of the Page class tree makes sense in this light. But...
Con:
- Some duplication of classes would occur: we'd basically have two RecentChangesPage classes. Would any other classes find themselves repeated? Certainly the ClientOutput would need things like old revisions and history list pages.
New output classes
Pro:
- Features such as site-wide linkbars or user-specific custom linkbars could be added as a new Output class. I have no idea how these could be plug-ins in current system.
Con:
- RssPage and other output methods will need some way of knowing that it can't do a Diff page, for example. This will need to be expansion-proof.
- RSS outputs a completely different kind of markup. It's not just a wrapping of template and whatever around the formatted Wiki markup. Two RecentChangesPage classes actually make sense here.
Hmm....
5 July 2005
The really big thing that's still missing is the database. Things like edit conflict and diff too, but while these are core features, they're not totally essential. With a database system and the basic UseMod parser, Moo should at least be ok to run on a local system as a personal notepad.
Oh and a system for user registration!
My To Do list at the moment is mainly cleaning up tasks – making variable names consistent and standardizing the way things are accessed.
1 July 2005
Tarquin: Today I've taken a tentative look at the script for Moo. It runs. It doesn't have a markup parser or a database, but you can edit and save a page, and there's basic support in place for things like category page listings. I need to spend some time getting reacquainted with the script so I understand it again. Fortunately, I've caught Mych's habit of leaving LOTS of comments. There's even POD too!
Old stuff
Rough notes for the class structure
- Page
- PageWiki: pages that open a page from the server
- Edit
- Edit Conflict
- Diff
- History
- PageWikiSpecial
- RecentChanges (maybe this & below given a base class, SpecialPage)
- Search
- Wanted Pages
- RandomPage
- Image Upload
- Edit
- Preferences
- Login
- List all pages
- Admin-Only
- Rename
- Ban IP
- Lock / unlock site
- Lock / Unlock page
- PageWiki: pages that open a page from the server
I had an idea to isolate behaviour for diff, edit, conflict, history in subclasses. The problem is that PageWikiSpecial children need that behavour too.
Possible solution: treat SpecialPages as a seperate tree of plug-in behaviours. (like bot brains).
Aside: can a class's parent be set at runtime? I suppose it can, since it's just
our @ISA = Block;
which sets parentage. Nothing stopping a
if( pants ) { our @ISA = Block; } else { our @ISA = NotBlock;}
and thus
- indentify requirement as Browse, Edit, Diff, Etc
- determine if SpecialPage
- spawn object of (desired SpecialPage) as child of requirement in 1
Would that count as REALLY twisted though?
Mychaeel: It can be done and is even documented as doable (in that the method inheritance namespace cache is documented to be cleared when it's done), but it's a last resort at most... if you can avoid stuff like that, do it.
Tarquin: Doh! Diff & Edit etc dont need to load in the generated content... just the saved wiki text. So if the object is if class "Diff" (say), then the fact that it doesnt load the generated behaviour is irrelevant!
Admin Pages
Maybe make a base class for Admin-only pages. Hmm. maybe not, a simple Admin-only flag somewhere.
http://c2.com/cgi/wiki?ShallowHierarchies Let's have a class PageAdmin which handles checking the viewer's admin status & displaying a message.
Home Page
slightly special because no page specified means go here... so not a class, but a special case in PageWiki
Actually, might as well make it a special case at the class level. The class for HomePage could be as simple as reading a config variable for which page to open.
Diff & History
not sure how to handle diff & history. probably:
- Page
- PageWiki
- History
- Diff
- PageWiki
The really clever thing to do would be to have History, Diff and EditConflict create a new PageWiki object that handles opening the second page.
This business that new() is non-special means that a class can have several creation functions, I suppose.
Mychaeel: Absolutely. (Naming convention is C++-like, by the way – start class names with capital letters, method names with lowercase letters. You can structure either by using capital letters inbetween.)
That means page classes could have a NewPage() and a NewNested() (or whatever. names subject to improvement). If a nested page is needed for diff, history, edit conflict, etc, create a new instance of PageWiki, but the NewNested obviously doesn't need to make headers, it just needs to open a file & display text.
Main function
Pondering two approaches:
method 1:
sub doBrowse { print &getHttpHeader(); print &getHtmlHeader(); print &getPageHeader(); print &getDocumentText(); # etc } method 2: <uscript> sub doBrowse { getHttpHeader(); getHtmlHeader(); getPageHeader(); getDocumentText(); # etc } # each function handles its own printing.
As I see it, the advantage of (2) is that I can choose whether to build up a $text and print in one go, or print pieces as it goes.
Mychaeel: I strongly suggest you build the entire page in memory first, then print it at once if appropriate. Just think of the Offline Wiki (plus, you can send a decent "Content-Length" HTTP header so browsers know in advance how much content to expect and can display working progress bars while loading).
New()
In wookee.pm, new() is only ever called with one argument. Therefore, is the second line in sub new() useful:
my $owner = shift;
It's causing me problems as I'm calling new( %PageVariables ) and the first piece of the hash gets eaten by it. Do i risk death or disaster by removing it?
Mychaeel: At one place in Wookee, new() takes a reference to an object's "owner" (to propagate information about embedded links and headers up the block nesting hierarchy). That's not a Perl built-in, it's merely my own design.
Mychaeel: Moo? I like the name.
Tarquin:
ZxAnPhOrIaN: I like moo too.
sub Browse { my $page = ' '; # need to know: # URL params # User name and status my %browseParams = ( page=>'Home', action=>'edit'); # determine which class to set $page equal to... foreach $subclass ( @registered ) { $page = $subclass→ChildofPage($page, %browseParams ); print "checking... $page\n"; # Page ## PageAdminTask ## PageWiki ## PageLogin ## PagePreferences } print $page→GenerateContent(); #print scalar @registered; }
hmm.... not ideal. I'm trying to think if there's ever a situation where different tasks might both want to perform, given the input, and one should take precedence. If not, might as well have classes return nothing unless they want to take on the task, and use some sort of a "while ..." construction instead of "foreach".