[Pangloss] Fwd: [openframe] [RFC] OpenFrame AppKit ideas
Steve Purkis
spurkis at quiup.com
Tue Apr 8 10:49:19 BST 2003
FYI:
Begin forwarded message:
> From: "James A. Duncan" <jduncan at fotango.com>
> Date: Mon Apr 7, 2003 6:34:32 pm Europe/London
> To: openframe at astray.com
> Subject: Re: [openframe] [RFC] OpenFrame AppKit ideas
> Reply-To: openframe at astray.com
>
>
> On Monday, April 7, 2003, at 03:29 PM, Steve Purkis wrote:
>
>> Hi,
>>
>> This is a [long] cross-posted message. Please watch replies.
>
> /me will refrain from cross posting - Steve, if you want to forward to
> pangloss then go ahead. I know nothing about the pangloss mailing
> list topic so its probably safer if I stay entirely on topic in a
> mailing list I am familiar with :-)
>
>> Application Kit
>> ---------------
>> OpenFrame::AppKit is a good example for OpenFrame, but it doesn't
>> quite meet my needs. For example, it limits:
>
> AppKit is largely just that - an example. I don't think that we
> actually use anything from it internally at Fotango anymore, and if we
> do its certainly well on its way to being refactored out of the > system.
>
>> + Support for different template engines (Petal)
>
> I'm not entirely sure how it limits it. Its easy to write another
> segment and plug it in to OpenFrame instead of the AppKit supplied TT2
> segment. This should be straightforward to do. If it does actually
> stop you from doing this I'd really like to hear what it is that stops
> you from doing it.
>
>> + Support for multiple languages / localisation
>
> We've managed to do this with OpenFrame - see
> http://www.cig.canon-europe.com. Again, I think its something that
> isn't built in to AppKit, but not that AppKit places arbitrary limits
> that stops it from happening.
>
>> + Cookie-less Sessions
>
> Certainly possible, we do this on the CIG site in places (for offline
> client integration), again we don't use AppKit heavily, if at all.
>
>> With some refactoring, I could achieve the above:
>> + write a Petal segment
>> + move hard-coded HTML out of Segments that use it,
>> sub-class & wheel in Locale::Maketext or Locale::gettext
>> + make SessionLoader segment abstract, then subclass for
>> Cookies, URL's, and Form params.
>
> Or not worry too much about having a pure abstract class, and merely
> subclass for URL and form parameters. Although without knowing
> exactly what you're trying to do I'm guessing URL and form parameters
> will look largely the same.
>
>> But I still wouldn't be happy, because while the AppKit does a lot
>> of things I like, it also does things I don't agree with. Like:
>
> The way Pipeline, therefore OpenFrame and therefore OpenFrame::AppKit
> is written doesn't suppose you would necessarily agree with it.
> _We're_ not entirely happy with it and we spent a lot of time writing
> it! :-)
>
>> I'm forced to invent a way of handling errors. I suppose I could
>> just stick them in the Session and let the template writer deal
>> with it. But then I gotta remove them on each request, and what
>> if I want to handle them before they get to the template?
>
> Thats fine, invent a way to handle errors. Its pretty hard to come up
> with a clever way of abstracting errors and localizing them, and have
> it cross cut your presentation and business logic. This is a
> difficult problem, and not one I've managed yet to solve or find a
> system that elegantly solves it for me. Rather than putting a two-bit
> hack into OpenFrame for this, we've left it out 'cause we've not yet
> encountered a sensible way of doing this without varying degree of
> hoopage. I suppose the best way from a pure programming perspective
> is to throw an exception that knows how to render itself in multiple
> languages. However, I hate message files that are based on key value
> pairs and thats the easiest way to be able to hand someone a file to
> translate. Come to think of it, I just hate presentation layers....
> :-)
>
>> Also, session storage should be more flexible - disk, memory, db,
>> etc. Could make a cache interface and pass in a Disk/Memory cache
>> to the SessionLoader as desired.
>
> I would recommend re-implementing a AppKit::SessionLoader to suit your
> needs. All the SessionLoader seems to do is get some sort of id from
> somewhere, and pass it to OpenFrame::AppKit::Session to handle loading
> and saving it. Currently OpenFrame::AppKit::Session uses
> Cache::FileCache, but I'm reasonably convinced (although I've not
> looked in a long time) that you can simply subclass and rewrite the
> store() and fetch() methods to use your own storage mechanism.
>
>> But most importantly, I'm not convinced entry points are a good
>> idea. They don't give enough control over the path of execution:
>
> Something we've done at fotango to great effect is to give the
> pipeline control over the flow of execution. Rather than trying to
> put the entirety of an application in one segment we split it up into
> its constituent parts. For example, we'd have an image loader, a
> title changer, a rotator, and a renderer. We'd stick them in a
> pipeline-of-pipelines that if drawn in xml might look something like
> this:
>
> <pipeline>
> <segment> album_loader </segment>
> <segment> image_loader </segment>
> <pipeline>
> <segment> decline_without_image </segment >
> <segment> rotate_an_image </segment>
> <segment> change_an_image_title </segment>
> <segment> render_an_image </segment>
> </pipeline>
> <pipeline>
> <segment> decline_without_album </segment>
> <segment> change_an_albums_title </segment>
> <segment> render_an_album </segment>
> </pipeline>
> </pipeline>
>
> The loader's will load the appropriate object when they can, and then
> the decliners sat at the top of the embedded pipelines return a
> Pipeline::Production if there isn't something the rest of the chain of
> execution down that particular pipeline can handle. By using this
> method rather than AppKit::App's fairly obscure entry-point
> mini-language, we've been able to get expressive classes that cause
> the model do one thing and one thing only. By joining them together
> in various ways we can quickly create new applications or by writing
> the merest sliver of code bring as-yet unused parts of the model into
> play.
>
>> + What if the user leaves some required form fields blank?
>> The entry point wouldn't fire, so you'd have to write
>> lots of entry points in order to tell them what they need
>> to fill in order to continue.
>
> When we have used AppKit::App we've tended to only put one or two
> elements of forms in the entry point - just enough to differentiate
> from the next entry point. We also post the form to itself, and
> redirect on success rather than error. That way the template can get
> at the arguments pointed to the system. If there are 0 length
> elements then we can put the error messages in the appropriate places.
> I'm not sure this is the best way of doing it, but it is _a_ way of
> doing it.
>
>> + What do you do if your parameter names are generated on
>> the fly?
>
> I think I'd ensure that one particular parameter was set in stone, and
> then write a subclass of Pipeline::Segment to deal with it rather than
> relying on anything that AppKit provided. AppKit::App is always going
> to cater for the general case. I can think of loads of edge cases
> that will break AppKit::App, which is why the inheritance hierarchy is
> reasonably long - you can step in before it gets to AppKit::App
> (after either Pipeline::Segment, or AppKit::DispatchOnURI) and deal
> with the situation in a more appropriate manner.
>
>> I'd prefer something that just dispatches a request to an object
>> based on the URI's path (ie: DispatchOnURI).
>
> You could certainly subclass DispatchOnURI now without much fear.
>
>> Alternative: Actions
>> --------------------
>>
>> So with that in mind, I thought maybe the OpenFrame AppKit could do
>> with some tools similar to Jakarta's Struts? For those of you not
>> familiar with Struts, I'll sum up what I think are some of its major
>> advantages:
>>
>> Actions
>> A thing that gets done, like logging in, making a payment,
>> or displaying a summary. These are usually short 'n sweet
>> ie, a thin layer passing messages to your app objects.
>
> Thats essentially what a Pipeline::Segment _should_ do. The hangman
> example is actually a pretty good example of this. All the business
> logic is held inside Games::GuessWord (or Games::WordGuess, I can
> never remember which), and it acts as the controller for the
> application. Although I'll admit that the Hangman example could use
> some refactoring and that it does kinda tie the controller and bits of
> the view together, its model is removed from the other two elements.
>
>> Validating 'Action Forms'
>> Each Action can have a Form with instance vars that are
>> filled out from the HTTP request params of the same name.
>> Or you can fill it out yourself, which is handy when your
>> params are generated on the fly from a db or something.
>> This form is then validated (ie: to make sure required
>> fields are present and not garbage).
>>
>> URL -> Action mapping
>> Each Action has a url, set in a config file.
>
> The other problem with this is a localization issue - if you take a
> piece of a URL, for example 'red.html', your french translater is
> probably going to want to translate this to 'rouge.html'. Depending
> on how tightly your URL's are bound to your actions you're going to
> either have to duplicate the section that calls into your application
> _or_ have some sort of pattern matching. I'm not particularly in fond
> of either of those two outcomes.
>
> In general I detest config files. Config files are mini-languages,
> and can be far better expressed in a programming language. I get the
> feeling I hold a different opinion on this from most people > however....
>
>> Action Forwards
>> Each Action can have one or more named 'forwarding' url's
>> which can be used to choose which template page to load.
>> success => "/success.html"
>> failure => "/failure.html"
>> these are also set in a config file.
>
> See above :-) Again with this I'd rather see the programmer decide
> what is going to happen, rather than having a configuration file that
> gets placed in obscure parts of the system.
>
>> Errors
>> Named flags that are looked up in a messages file.
>> Which means no more hard-coded HTML, and translatable
>> message files.
>
> I've bashed out my dislike of all exception systems above :-).
>
>> So if we start off with the above, and mix it with the things I
>> like about the AppKit, and Perl in general, bake for 3 hours and
>> let cool, we end up with something like this...
>>
>> OpenFrame::AppKit::Action <abstract>
>> + dispatch_on_uri()
>> validates & executes this action.
>> + validate()
>> validates Request in store, producing an ActionForm.
>> + execute()
>> executes the action...
>> On success produces a Template (similar to idea of forwarding
>> could get template from a config file, for example)
>> Any Messages or Errors are stored in the pipe.
>>
>> OpenFrame::AppKit::Action::Form
>> + simple hash. will probably look like the Request, but
>> may differ if validate() processes things, like
>> stripping .X/.Y off of image inputs.
>
>> OpenFrame::AppKit::Session
>> + reserve the names:
>> errors
>> error.*
>> for errors
>> + add localisation hooks here?
>>
>> OpenFrame::AppKit::Errors
>> + request-scope error flags
>> + this class would look just like Session except:
>> has_errors()
>> + add localisation hooks here?
>>
>> OpenFrame::AppKit::Template
>> + Template name, and other params potentially. Would be
>> used by TT2, which currently gets template name from the
>> Request.
>>
>> OpenFrame::AppKit::Segment::TemplateLoader <abstract>
>> + Can be sub-classed for your favourite templating system
>> (TT2, Petal, etc.).
>> + Looks for OpenFrame::AppKit::Template object in Store.
>> + Creates template processing vars with scoping like this:
>> Session no prefix
>> Errors 'error.' prefix
>> So it's easy to add other scoped variables. all you need
>> to do is reserve a prefix (or sub-class)...
>>
>> OpenFrame::Response::NotFound
>> + HTTP 404 error, replacing hard-coded version in
>> OpenFrame::Segment::ContentLoader
>> ? Not ideal, but easier to localise than current version.
>>
>> OpenFrame::Response::InternalError
>> + HTTP 500 error, replacing hard-coded version in
>> OpenFrame::Segment::HTTP::Response
>> ? Not ideal, but easier to localise than current version.
>
> One of the things I like _least_ about OpenFrame is the proliferation
> of dumb data objects that it seems to encourage. This is partly my
> fault because I also dislike the idea of introducing named keying into
> a Pipeline::Store,subclass but of course people have gone and done
> this anyway :-)
>
> Lots of these things seem like just that without much in the way of
> behaviour which causes me to feel a little disturbed about them. The
> current OpenFrame gets away with just a couple of data objects:
> OF::Request, OF::Response, and OF::Cookies (OF::Cookie is just a
> wrapper around CGI::Cookie, but at least that gives it *some*
> behaviour :-) and I'd be worried about setting a trend by having more
> data objects than behavioral objects in the system.
>
>> Localisation
>> ------------
>> I'm still a bit fuzzy on this. AFAICS, localisation needs to
>> be done in three places:
>>
>> 1. Reporting Application Errors (404, 500, etc.)
>> 2. Choosing which template to process
>> 3. Displaying messages to the user from a template
>>
>> Which means we need hooks in these places. But perhaps the
>> problem could be solved in general by a Localisation segment
>> that puts something in the Store that other segments can use?
>
> We do that at Fotango for CIG as well as other systems that are being
> worked on at the moment. We have a Site-map class that maps URLs to
> abstract paths in the system, and back to templates. The site-map
> also carries all the text and error messages that are used by the
> templates. The problem with it is that although the information is
> kept in one place and one place only it also seems to be a pain to
> remember just where that place is. Again, I hate presentation layers
> so I'm probably not best to comment on this mechanism as opposed to
> any other - I hate them all :-)
>
> For now I remain unconvinced. Not that what you're proposing isn't
> valuable, but I don't think that AppKit is the right place for it
> straight away. This is the main reason that some of the things that
> we've done at Fotango similar to what you're suggesting haven't been
> rolled into AppKit (well, that and general time limitations).
>
> However, I think that OpenFrame as-a-whole would benefit from
> something along these lines, and would allow you to do it easily (in
> as much as openframe on its own gives you a good starting point, some
> useful scaffolding, and won't get in the way too much).
>
> Hmm, dictionary.com -> thesaurus -> strut -> Lintel? Crossbar? Joist?
> Girder?
>
> Regards,
> James.
>
>
> _______________________________________________
> http://www.astray.com/mailman/listinfo/openframe
More information about the Pangloss
mailing list